Search This Blog

Tuesday, 3 September 2013

Exception Handling in Spring REST calls

I earlier did a post on error handling in Spring web applications. It was related to generating views for errors. But in a REST application we would prefer to return error codes or error JSON message. While the earlier style can be still used here to write out an appropriate JSON error response, Spring MVC provides other mechanisms to handle errors.
Consider the below method:
@RequestMapping(value = "{id}", method = RequestMethod.GET)
   public @ResponseBody
   User getUser(@PathVariable final int id) {
      throw new SecurityException();
   }
The response would be:
2013-06-15 19:28:42 DEBUG RestTemplate:78 - Created GET request for "http://loca
lhost:8080/SampleRest/api/user/3"
2013-06-15 19:28:42 DEBUG RestTemplate:528 - Setting request Accept header to [a
pplication/json]
2013-06-15 19:28:43 WARN  RestTemplate:486 - GET request for "http://localhost:8
080/SampleRest/api/user/3" resulted in 500 (Internal Server Error); invoking err
or handler
Exception in thread "main" org.springframework.web.client.HttpServerErrorExcepti
on: 500 Internal Server Error
 at org.springframework.web.client.DefaultResponseErrorHandler.handleError(Defau
ltResponseErrorHandler.java:92)
 at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate
.java:494)
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:451)
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
 at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:2
07)
 at test.client.UserRestClient.getUser(UserRestClient.java:27)
 at test.client.UserRestClient.main(UserRestClient.java:81)
The response is always an Internal server error. This is actually a valid behavior. But can we customize the same? For example I would like to return a different error code if the failure was at a database level.
Spring provides us with the ability to customize the mapping between response status and exceptions. Let say we would like to handle all Security Exceptions with Bad Request error code.
   @ResponseStatus(HttpStatus.BAD_REQUEST)
   @ExceptionHandler({ SecurityException.class })
   public void handleSecurityException() {
      // method called when a security exception occurs
   }
I added the method in my controller. Now the error at the client changes to the below:
2013-06-15 19:43:32 DEBUG RestTemplate:78 - Created GET request for "http://loca
lhost:8080/SampleRest/api/user/3"
2013-06-15 19:43:32 DEBUG RestTemplate:528 - Setting request Accept header to [a
pplication/json]
2013-06-15 19:43:33 WARN  RestTemplate:486 - GET request for "http://localhost:8
080/SampleRest/api/user/3" resulted in 400 (Bad Request); invoking error handler
Exception in thread "main" org.springframework.web.client.HttpClientErrorExcepti
on: 400 Bad Request
 at org.springframework.web.client.DefaultResponseErrorHandler.handleError(Defau
ltResponseErrorHandler.java:90)
When a SecurityException occurs at any of the controller's methods, the control will pass to the method handleSecurityException. The method is annotated with a ResponseStatus annotation. Accordingly the Exception handler will write the response status 400 to the response, completing it.
This is seen in the server error logs:
2013-06-15 19:43:33 DEBUG ExceptionHandlerExceptionResolver:132 - Resolving exce
ption from handler [public com.test.controller.User com.test.controller.RestServ
iceController.getUser(int)]: java.lang.SecurityException
2013-06-15 19:43:33 DEBUG ExceptionHandlerExceptionResolver:273 - Invoking @Exce
ptionHandler method: public void com.test.controller.RestServiceController.handl
eSecurityException()
2013-06-15 19:43:33 DEBUG ServletInvocableHandlerMethod:123 - Invoking [handleSe
curityException] method with arguments []
2013-06-15 19:43:33 DEBUG ServletInvocableHandlerMethod:129 - Method [handleSecu
rityException] returned [null]
2013-06-15 19:43:33 DEBUG DispatcherServlet:957 - Null ModelAndView returned to 
DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed requ
est handling
The DispatcherServlet delegates such exception handling to the ExceptionHandlerExceptionResolver Earlier we have seen how to create an exception resolver. In case of our exceptionHandler methods too, we can easily redirect to a View if we need to include a response body. However there is one difference. The earlier resolver was applicable at a global level whereas the @ExceptionHandler annotated method is only active for the particular Controller it is defined in.
Spring also provides a ResponseStatusExceptionResolver configured with the DispatcherServlet. This class is responsible for handling any exception that is annotated with ResponseStatus
For e.g. consider the below exception:
@ResponseStatus(value = HttpStatus.EXPECTATION_FAILED)
class DummyException extends RuntimeException {
}
If any method now throws a DummyException, the response will include the EXPECTATION_FAILED error code.
2013-06-15 20:00:24 WARN  RestTemplate:486 - GET request for "http://localhost:8
080/SampleRest/api/user/3" resulted in 417 (Expectation Failed); invoking error 
handler
Exception in thread "main" org.springframework.web.client.HttpClientErrorExcepti
on: 417 Expectation Failed
There is a very good link that discusses on the various mechanisms to handle exceptions in REST web services.

No comments:

Post a Comment