Spring REST: Exception handling vol. 1
Table of contents
- Spring REST: Exception handling vol. 1
- Spring REST: Exception handling vol. 2
- Spring REST: Exception handling vol. 3
Hi everyone, it’s time to continue publish new articles in my blog. So I’m happy to announce that I’m planning to write a couple of technical series of posts. In the current post I’m going to start talking about a Spring REST Exception handling. Spring suggests to us several ways of REST exception handling, but I want to concentrate your attention on two of them:
- @ExceptionHandler on a @Controller level
- @ExceptionHandler on a @ControllerAdvice level
All code examples will be developed with an application which I used in my previous posts about REST services. JQuery will provide interaction with REST services on a client side.
So after the concise introduction I want to summarize. We will consider three examples of REST exception handlers. Each of these three cases will describe solution of some real situation which can occur in any project. All development will be done on a top of already existed application.
Preparation
The first thing which I want to do – it’s to add MessageSource to the application. It’s not very hard and I don’t want to stop on this in details, because I have explained how to do this in a separate post. A purpose of the MessageSource is to store error messages which I want to return to a client in a case if exception was thrown.
So here is a messages.properties file:
error.bad.smartphone.id = Smartphone can't have id:
After MessageSource was successfuly added we can continue with exception handling on a @Controller level.
Exception handling
In this paragraph I want to highlight code snippets where exception can occur. Let’s examine some methods from the SmartphoneController.
... @RequestMapping(value="/edit/{id}", method=RequestMethod.GET) public ModelAndView editSmartphonePage(@PathVariable int id) { ModelAndView mav = new ModelAndView("phones/edit-phone"); Smartphone smartphone = smartphoneService.get(id); mav.addObject("sPhone", smartphone); return mav; } ... @RequestMapping(value="/edit/{id}", method=RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Smartphone editSmartphone(@PathVariable int id, @Valid @RequestBody Smartphone smartphone) { smartphone.setId(id); return smartphoneService.update(smartphone); } ... @RequestMapping(value="/delete/{id}", method=RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Smartphone deleteSmartphone(@PathVariable int id) { return smartphoneService.delete(id); } ...
These three methods have one common feature – @PathVariable int id. This circumstance is important, because Spring documentation said that if method argument annotated with @PathVariable can’t be casted to specified type (in our case to int), it will be exposed as String. Hence it can cause a TypeMismatchException.
To handle this I will use @ExceptionHandler annotation on @Controller level. Such approach suits for this situation as no one else. I just need to make 2 changes in the SmartphoneController:
- Add MessageSource field
- Add exception handler method
... @Autowired private MessageSource messageSource; ... @ExceptionHandler(TypeMismatchException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) @ResponseBody public ErrorInfo handleTypeMismatchException(HttpServletRequest req, TypeMismatchException ex) { Locale locale = LocaleContextHolder.getLocale(); String errorMessage = messageSource.getMessage("error.bad.smartphone.id", null, locale); errorMessage += ex.getValue(); String errorURL = req.getRequestURL().toString(); return new ErrorInfo(errorURL, errorMessage); } ...
Let’s consider the method. The @ExceptionHandler annotation has the argument – TypeMismatchException, this mean that the method will be triggered when the exception will occur. The @ResponseStatus annotation used for specifying of particular response status code.
You probably noticed that the method returns ErrorInfo. Here is nothing difficult it is a class for any kind of error which need to inform a client about error cause. So the class looks like:
public class ErrorInfo { private String url; private String message; public ErrorInfo(String url, String message) { this.url = url; this.message = message; } //Getters and setters are omitted }
Using of this class gives to us two main advantages: we can provide a URL which caused an exception and we can provide appropriate error message.
Now let’s try to see what we have in case when I try to access some URL with unacceptable id.
You can see on the screenshot that the URL with bad id was handled as I specified on the @Controller level. In the next article I will talk about some exceptions which we can place on @ControllerAdvice level.