I am trying to use an interceptor to introspect and change exceptions that occur on the backend. The interceptor looks something like this:
public class ApplicationErrorInterceptor {
private static final Logger LOGGER = Logger.getLogger(ApplicationErrorInterceptor.class);
#AroundInvoke
public Object handleException(InvocationContext context) {
try {
return context.proceed();
} catch (Exception ex) {
LOGGER.errorf(ex, "An unhandled exception occured!");
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("some custom 500 error text")
.build();
}
}
}
And the service using this interceptor looks something like this:
#Path("/somepath")
#Interceptors({ApplicationErrorInterceptor.class})
public interface SomeRestService {
#POST
#Path("getResponse")
Response getResponse();
#POST
#Path("getVoid")
void getVoid();
}
Assume the implementation for both of these methods throw an exception. I expect the exception to be mapped to a 500 server error with the supplied custom message in both cases. Unfortunately, the method returning void will get mapped to a 204 No Content response. If I remove the interceptor altogether, the default 500 server error occurs, which is at least the correct status code, but I lost the error customization
Related
I have an Microservice and it gets an response from another. And based on the response I get I need to respond accordingly. I have no complete List of Error code I can receive, so the question is - can I generate error codes on the fly for my own response? From what I saw in spring the responses are predefined in code. I need to be flexible.
For example:
I receive a 409 I will respond with 409
I receive a 400 I will respond with 400
I receive a XXX code I will respond with XXX.
Try this code: (Sample code)
#RequestMapping(value = "/validate", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<ErrorBean> validateUser(#QueryParam("jsonInput") final String jsonInput) {
int numberHTTPDesired = 400;
ErrorBean responseBean = new ErrorBean();
responseBean.setError("ERROR");
responseBean.setMessage("Error in validation!");
return new ResponseEntity<ErrorBean>(responseBean, HttpStatus.valueOf(numberHTTPDesired));
}
I have worked on such a use case using the following concept. Try to create a generic exception across micro services. Take 2 params in the exception as error message and another one as error code. Throw the exception from the service being called and catch the same exception in the calling service in the rest template or feign client call.
public class MyException extends Exception {
private String errorCode;
public MyException() {
super();
}
public MyException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
}
--
try {
return myApiService.getUser();//call to myApiService microservice
} catch (MyException e) {
logger.error("Error: {}", e.getMessage());
throw new MyException(e.getMessage(), e.getCode());
}
I was referring few articles to create global exception handler using #ControllerAdvice for my rest api project using spring. The purpose of this is to send proper formatted response to the client in the case of exception occurred. In some articles they have added Throwable or Exception in global exception handler.
Should I replace it with RunTimeException as this block is for exception occurred at runtime?
Exception Handler code:
#ControllerAdvice
public class GlobalExceptionHandler{
#ExceptionHandler(NoDataFoundException.class)
#ResponseStatus(code=HttpStatus.NOT_FOUND)
public ResponseEntity<ErrorResponse> handle(NoDataFoundException ex){
ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value());
ResponseEntity<ErrorResponse> response = new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
return response;
}
..... more methods to handle custom exceptions
#ExceptionHandler(Exception.class)
#ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<ErrorResponse> handle(Exception ex){
ErrorResponse errorResponse = new ErrorResponse("Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR.value());
ResponseEntity<ErrorResponse> response = new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
}
ErrorResponse code:
public class ErrorResponse {
private String message;
private int statusCode;
public ErrorResponse(String message, int statusCode) {
super();
this.message = message;
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
}
References:
https://dzone.com/articles/exception-handling-in-spring-boot-rest-web-service
https://github.com/in28minutes/spring-boot-examples/tree/master/spring-boot-2-rest-service-exception-handling
Should I replace it with RunTimeException as this block is for
exception occurred at runtime?
To make sure that you catch any exception thrown and never handled by your components or any exception handler with a more typed exception than Exception, you should have a handler for Exception.
A handler for RuntimeException is not enough because checked exception are also thrown at runtime and if the method declarations of your high level components specify throws Exception or throws "any checked exception", a checked exception could be propagated until the client or here the container that will apply a default behavior.
For example imagine this rest controller method declaration that could be make this situation to happen :
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<Foo> getOne(#PathVariable long id) throws Exception {
// ....
}
And to override this default Spring behavior, you will want to add a handler for Exception.
Of course it doesn't mean that declaring a handler only for Exception is the way but you may have some exception with no specific handling and for that a generic handling is fine.
Honestly, having an exception handler handle Exception seems a bit lazy to me. Since Exception is checked, you should be responsible for handling or recovering from the error. If you're not able to recover from the error, or the circumstance you're in prevents you from writing code that allows you to elegantly recover, it should be re-thrown in a RuntimeException instead to indicate the issue.
Of course, exception handlers serve two purposes:
They give you the power to define a standard of how error responses look and what they contain in detail.
They give you the ability to log the error so you can go back later and fix it.
I'd strongly encourage the pattern of re-throwing checked Exceptions as unchecked, and handling those in exception handlers. As a fail-safe catch-all, you can use a generic exception handler for Exception to catch all of the spots that weren't converted over, and to log what happened.
It should never be the case that you as a developer allow an Exception to propagate all the way to the top without explicit reason.
I am using jersey for REST web services.
I am handling all 404 responses by throwing NotFoundException(Package com.sun.jersey.api) whenever I don't get any object from service layer.
e.g.
#GET
#Path("/{folderID}")
#Produces(MediaType.APPLICATION_JSON)
public Response getFolder(#PathParam("folderID") int folderID) {
.
.
.
Folder folderObj = folderService.getFolder(folderID);
if(folderObj == null){
throw new NotFoundException("Folder with ID '"+folderID+"' not found.");
}
}
I have written ExceptionMapper for this exception.
#Provider
public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
public Response toResponse(NotFoundException ex) {
ErrorMesage errorMessage = new ErrorMessage();
errorMessage.setCode(Status.NOT_FOUND.getStatusCode());
errorMessage.setMessage(ex.getMessage());
return Response.status(Status.NOT_FOUND)
.entity(errorMessage)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
So When I give unknown folder ID as path parameter, exception is thrown but code in NotFoundExceptionMapper is not invoked. (I can see exception message in response but as 'plain text', even though in mapper I am returning response in JSON; and debug break point is also not hit).
Also, Above exception mapper is invoked when I enter incorrect resource name in URI, but not for incorrect path param.
I have also added exception mapper like below to respond to all other exceptions.
public class GenericExceptionMapper implements ExceptionMapper<Throwable>{
public Response toResponse(Throwable ex) {
ErrorMessage errorMessage = new ErrorMessage();
errorMessage.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
errorMessage.setMessage(ex.getMessage());
return Response.status(errorMessage.getCode())
.entity(errorMessage)
.type(MediaType.APPLICATION_JSON)
.build();
}
Above code is called whenever any exception (other than mapped exceptions) is thrown and I get proper JSON response.
So what is wrong with NotFoundException here?
I have googled about this and also looked into source of NotFoundException but didn't find anything useful, please guide me on this.
A snippet from the jersey ServerRuntime class. It has a special logic that if the Exception is an instance of WebApplicationException and has a body, it does not go to the exception mappers at all.
private Response mapException(Throwable originalThrowable) throws Throwable {
if (throwable instanceof WebApplicationException) {
WebApplicationException webApplicationException = (WebApplicationException)throwable;
}
this.processingContext.routingContext().setMappedThrowable(throwable);
waeResponse = webApplicationException.getResponse();
if (waeResponse.hasEntity()) {
LOGGER.log(java.util.logging.Level.FINE, LocalizationMessages.EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), throwable);
return waeResponse;
}
long timestamp = this.tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
ExceptionMapper mapper = this.runtime.exceptionMappers.findMapping(throwable);
}
I have question that interest me.
Assume that I have some rest controller and some rest client writing in javascript. This client send request to a controller and during a processing occur some error. How should behave controller in this situation? Should return null? or string with message?
For example, We have controller like this:
#RequestMapping("/user", method = RequestMethod.POST)
public #ResponseBody String createUser(User user) {
try {
userService.create(user);
} catch(UserCreationException e) {
}
}
This is very simple example but is many different examples of controllers like controller which return some resources or only change state on the server side and I don't know what to do when occur error.
in improving developer(your consumers) experience , it is a good idea to respond with appropriate error messages on the response body in addition to the Http status code.
Here is an example with spring, mainly throw an exception that you can deal with by extending ResponseEntityExceptionHandler #ControllerAdvice
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}
#Controller
#RequestMapping("/XXXXXs")
public class DoctypesController {
#RequestMapping( method = RequestMethod.GET , value="/xxx")
public ResponseEntity<?> getXXXXXX(HttpServletRequest request) {
if (XXX == null ) {
throw new ResourceNotFoundException("XXXX Not found for);
}else{
response = buildResponse(xxxx)
}
return response;
}
}
#ControllerAdvice
public class XXXXEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { ResourceNotFoundException.class })
protected ResponseEntity<Object> handleMissingResource(RuntimeException ex, final WebRequest request) {
HttpStatus status = HttpStatus.NOT_FOUND;
return new ResponseEntity<Object>(new Error(String.valueOf(status.value()), status.getReasonPhrase(),ex.getMessage()),status);
}
}
According http specifications, the server must return a error code >= 500 in case of internal error during processing.
If the error is caused because the client did a wrong request : the server must return a error code >= 400 and < 500
Of course, on client side you must take care to handle those errors properly (i.e. displaying a friendly error message or something like that).
You should really use the HTTP Error codes and handle the HTTP error codes using your client-side technology, ie. JavaScript in your case.
For example: given a user who is unauthorised to read/access a Resource, then the 403 error code should be returned to the client. By using the standard HTTP/REST Error codes, you conform to an API that can be understood by any client, whether JavaScript or something else.
With Spring MVC and Rest controllers, it's really easy. Create a simple class for your Exception and annotate the class with the HTTP Error code, e.g. #ResponseStatus(value = HttpStatus.FORBIDDEN) for a 403 error. Then in your Controller, you can throw the exception which would in turn return the HTTP error code.
I want to catch all unexpected Exceptions in a jersey rest service.
Therefore i wrote an ExceptionMapper:
#Provider
public class ExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
private static Logger logger = LogManager.getLogManager().getLogger(ExceptionMapper.class.getName());
#Override
public Response toResponse(Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal error").type("text/plain").build();
}
}
The mapper catches really all exceptions. Therefore i can't write:
public MyResult getById(#PathParam("id")) {
if (checkAnyThing) {
return new MyResult();
}
else {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
This is catched by the Mapper. Now i have to write:
public Response getById(#PathParam("id") {
if (checkAnyThing) { {
return Response.ok().entity(new MyResult()).build();
}
else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Is this the correct way to catch all unexpected exceptions and also return errors (error codes) in jersey? Or is there any other (more correct) way?
WebApplicationException has a getResponse from which we can get the Response. So you can check for a WebApplicationException in your mapper. Maybe something like
#Override
public Response toResponse(Throwable error) {
Response response;
if (error instanceof WebApplicationException) {
WebApplicationException webEx = (WebApplicationException)error;
response = webEx.getResponse();
} else {
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Internal error").type("text/plain").build();
}
return response;
}
That way an instance of WebApplicationException thrown will just return the default response. This will actually handle some other exceptions also, not thrown explictly by your application. WebApplicationException has a few other exception under its hierarchy that are thrown by JAX-RS, for which predefined response/status codes are wrapped.
Exception Status code Description
-------------------------------------------------------------------------------
BadRequestException 400 Malformed message
NotAuthorizedException 401 Authentication failure
ForbiddenException 403 Not permitted to access
NotFoundException 404 Couldn’t find resource
NotAllowedException 405 HTTP method not supported
NotAcceptableException 406 Client media type requested
not supported
NotSupportedException 415 Client posted media type
not supported
InternalServerErrorException 500 General server error
ServiceUnavailableException 503 Server is temporarily unavailable
or busy
That being said, we could explicitly throw any of these exceptions in our code, just to give it more semantic value.
Generally speaking though, the example above may be unnecessary, unless you want to alter the response message/status code, as one can from the table above, the hierarchy of exceptions already have some general mapping. And in most cases, unexpected exceptions will already be mapped to InternalServerErrorException