Different ways of validating @RequestBody in Spring MVC 3.2 with @Valid annotation
In Spring MVC the @RequestBody annotation indicates a method parameter should be bound to a body of the request. @RequestBody parameter can treated as any other parameter in a@RequestMapping method and therefore it can also be validated by a standard validation mechanism. In this post I will show 2 ways of validating the @RequestBody parameter in your Spring MVC application.
In my sample application I want to create a new Task with not blank name and description. In order to do it I create an API endpoint that supports POST method and accepts Task as JSON.
Tip: To bootstrap the application I used: spring-mvc-quickstart-archetype .
Let's start with the task:
To handle the task we need a @Controller:public class Task { @NotBlank(message = "Task name must not be blank!") private String name; @NotBlank(message = "Task description must not be blank!") private String description; public Task() { } public Task(String name, String description) { this.name = name; this.description = description; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
The next thing we need to do is the validation. So let's do it.@Controller @RequestMapping(value = "task") public class TaskController { @RequestMapping(value = "", method = RequestMethod.POST) @ResponseBody public Task post(Task task) { // create a task } }
Validation with @ExceptionHandler
As of Spring 3.1 the @RequestBody method argument can be annotated with @Valid or @Validated annotation to invoke automatic validation. In such a case Spring automatically performs the validation and in case of error MethodArgumentNotValidException is thrown. Optional @ExceptionHandler method may be easily created to add custom behavior for handling this type of exception. MethodArgumentNotValidException holds both the parameter that failed the validation and the result of validation. Now, we can easily extract error messages and return it in an error object as JSON.
@Controller @RequestMapping(value = "task") public class TaskController { @RequestMapping(value = "", method = RequestMethod.POST) @ResponseBody public Task post(@Valid @RequestBody Task task) { // in case of a validation error, MethodArgumentNotValidException will be thrown. } @ExceptionHandler @ResponseBody @ResponseStatus(value = HttpStatus.BAD_REQUEST) public Error handleException(MethodArgumentNotValidException exception) { return new ApiErrors(exception.getBindingResult()); } }
Exception handler method does not need to be located in the same controller class. It can be a global handler for all you API calls.
Validation with Errors/BindingResult object
As of Spring 3.2 @RequestBody method argument may be followed by Errors object, hence allowing handling of validation errors in the same @RequestMapping. Let's look at the code:
@Controller @RequestMapping(value = "task") public class TaskController { @RequestMapping(value = "", method = RequestMethod.POST) @ResponseBody public ResponseEntity post(@Valid @RequestBody Task task, Errors errors) { if (errors.hasErrors()) { return new ResponseEntity(new ApiErrors(errors), HttpStatus.BAD_REQUEST); } return new ResponseEntity(task.save(), HttpStatus.CREATED); } }
Both approaches produce the same result in the above example. Which is better? I don't know yet. And do you?