Alexey Zvolinskiy

About Alexey Zvolinskiy

Alexey is a test developer with solid experience in automation of web-applications using Java, TestNG and Selenium. He is so much into QA that even after work he provides training courses for junior QA engineers.

Spring REST: Exception handling vol. 2

This is a second article from the series about REST Exception handling with Spring. In my previous post I have described how to organise the most simple exception handling in a REST service. This time I will go further and I will show you when you’d better to use exception handling on a @ControllerAdvice level.Spring-Exception-Handling

INTRO

Before I will start the technical part of the post, I need to concider a situation when we would better to use an exception handling on a @ControllerAdvice level.
Usually one controller is responsible for entire logic related to one type of entities. That’s mean that if I have some EntityController class, it will contain all CRUD (create, read, update, delete) operations with the entity and maybe some extra logic if it is required. Let’s examine three operations: read, update, delete.

The read operation returns some particular entity depending on ID which we pass to it as an argument. if the entity doesn’t exist the read operation returns null. The update / delete operation updates / deletes a particular entity respectively. And each of these two operations include the read operation, because before an entity will be updated / deleted we need to ensure that it exist in a system.

It’s a normally practice when an entity is not found during the update / delete operation an application will throw an EntityNotFoundException. An exception handling in this case will be very straightforward. The application have to return an information to a client:

  • Response Header: 404
  • A link which caused the exception
  • Error message: There is no Entity with id: N

This is a most simple response structure for a such exceptions. So regardless how many different entity classes you have in an application, because you can handle similar kind of exception (e.g. no such entity) in the same way. This became possible thanks @ControllerAdvice annotation.

Exception handling on @ControllerAdvice level

Practical part of the article will be based on the application form the last tutorial.

Firstly I need to add an error message into the messages.properties file:

error.no.smartphone.id = There is no Smartphone with id:

After this let’s look on the controller methods which are interesting for us in topic of this article.

...
	@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 methods include calls of the SmartphoneService. And implementation of the SmartphoneService contains methods which can throw a SmartphoneNotFoundException.

@Service
@Transactional(rollbackFor = { SmartphoneNotFoundException.class })
public class SmartphoneServiceImpl implements SmartphoneService {

	@Autowired
	private SmartphoneRepository smartphoneRepository;

	@Override
	public Smartphone create(Smartphone sp) {
		return smartphoneRepository.save(sp);
	}

	@Override
	public Smartphone get(Integer id) {
		Smartphone sp = null;
		if (id instanceof Integer)
			sp = smartphoneRepository.findOne(id);
		if (sp != null)
			return sp;
		throw new SmartphoneNotFoundException(id);
	}

	@Override
	public List getAll() {
		return smartphoneRepository.findAll();
	}

	@Override
	public Smartphone update(Smartphone sp) {
		Smartphone sPhoneToUpdate = get(sp.getId());
		sPhoneToUpdate.update(sp);
		return sPhoneToUpdate;
	}

	@Override
	public Smartphone delete(Integer id) {
		Smartphone sPhone = get(id);
		smartphoneRepository.delete(id);
		return sPhone;
	}

}

Here is a code of SmartphoneNotFoundException:

public class SmartphoneNotFoundException extends RuntimeException {

	private static final long serialVersionUID = -2859292084648724403L;
	private final int smartphoneId;

	public SmartphoneNotFoundException(int id) {
		smartphoneId = id;
	}

	public int getSmartphoneId() {
		return smartphoneId;
	}

}

And finally I can move to the @ControllerAdvice.

@ControllerAdvice
public class RestExceptionProcessor {

	@Autowired
	private MessageSource messageSource;

	@ExceptionHandler(SmartphoneNotFoundException.class)
	@ResponseStatus(value=HttpStatus.NOT_FOUND)
	@ResponseBody
	public ErrorInfo smartphoneNotFound(HttpServletRequest req, SmartphoneNotFoundException ex) {
		Locale locale = LocaleContextHolder.getLocale();
		String errorMessage = messageSource.getMessage("error.no.smartphone.id", null, locale);

		errorMessage += ex.getSmartphoneId();
		String errorURL = req.getRequestURL().toString();

		return new ErrorInfo(errorURL, errorMessage);
	}

}

The exception handler method returns ErrorInfo object. You can read more about it in the previous post about Exception Handling on a @Controller level.

In this way we can collect all similar exceptions in one place, just by adding extra exception classes into @ExceptionHandler annotation. This approach makes a code maintenance more easier within an entire application.

Demonstration of the example:

Spring-Exception-Handling-ControllerAdvice-1

Note: I made the request with the id value = 356, but there wasn’t any record in a database which correspond to this id value. This circumstance cause the exception.
 

Reference: Spring REST: Exception handling vol. 2 from our JCG partner Alexey Zvolinskiy at the Fruzenshtein’s notes blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


6 × six =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close