Spring REST Error Handling

Spring REST Error Handling

Spring framework provides three ways to handle error response while processing a request in the controller class

  • Locally in the method handling a specific request
  • Specific controller method level
  • Global controller level

Locally using @ResponseEntity

The following code only updates the Person if it already exists otherwise responds with a HTTP Status Code 400 that is Bad Request. The ResponseEntity takes the object of response dto as well as the HTTPStatus object.

@PutMapping("/person")
ResponseEntity<PersonDto> update(@RequestBody PersonDto personDto) {
	
	HttpStatus status;
		
	if (null != personService.findById(personDto.getId())) {
		personDto = personService.update(personDto);
		status = HttpStatus.OK;
	} else {
		status = HttpStatus.BAD_REQUEST;
	}
	
	return new ResponseEntity<PersonDto>(personDto, status);
}

Controller Level Using @ExceptionHandler

We can create a method to handle exceptions within a controller and annotate it with @ExceptionHandler. Find below the sample code using this approach.

First we create a class ErrorMessage to encapsulate the error message.

public class ErrorMessage {

	private String status;
	
	private String message;

	public ErrorMessage(String status, String message) {
		this.status = status;
		this.message = message;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

Then we create the method to handle ValidationException thrown by other request handling methods.

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler
ErrorMessage exceptionHandler(ValidationException e) {
	return new ErrorMessage("400", e.getMessage());
}

Finally the methods handling the requests can throw ValidationException on validation failure. The below code snippet throws exception if not all mandatory values are provided while creating a resource.

@PostMapping("/person")
PersonDto create(@RequestBody PersonDto personDto) {
	if ((null != personDto.getFirstName()) && (null != personDto.getLastName())) {
		return personService.create(personDto);
	} else {
		throw new ValidationException("All mandatory values are not provided.");
	}
}

Globally Using @ControllerAdvice

To create a global handler for all controllers create a new class and annotate it with @ControllerAdvice. Then create a method similar to the method we created in last section but with an extra annotation @ResponseBody.

@ControllerAdvice
public class ControllerExceptionHandler {

	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(ValidationException.class)
	ErrorMessage exceptionHandler(ValidationException e) {
		return new ErrorMessage("400", e.getMessage());
	}
}

Conclusion

Error handling for web services is a more elaborate exercise where we need to determine that besides setting up an effective handler we need to also comply with the overall architectural principles of any organization. This blog highlights the various ways in which Spring facilitates different levels of exception handlers which we can leverage for implementing the exception handlers in our applications.