SyntaxHighlighter

Sunday, January 6, 2013

Spring WebMVC 3.2 and Validation

Spring WebMVC 3.2 and Validation

Spring 3.2 WebMVC has a neat new feature regarding validation and error handling.  Let's say we have the following use case;
- objects are POSTed as JSON
- objects are @Valid on arrival
- any validation errors are returned as parsed JSON responses inside the body of the reply so that the client can process

so, first we have our normal request processor

package de.incompleteco.spring.web.controller;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import de.incompleteco.spring.web.domain.SimpleEntity;

@Controller
public class ValidatorController {

 @RequestMapping(value="/simpleEntity",method=RequestMethod.POST)
 public SimpleEntity process(@Valid SimpleEntity simpleEntity) throws Exception {
  System.out.println("got it here");
  simpleEntity.setProcessed("true");
  return simpleEntity;
 }
 
}

and here's the entity

package de.incompleteco.spring.web.domain;

import java.io.Serializable;

import javax.validation.constraints.NotNull;

public class SimpleEntity implements Serializable {

 private static final long serialVersionUID = 1L;

 @NotNull
 private Long id;
 
 private String processed;

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getProcessed() {
  return processed;
 }

 public void setProcessed(String processed) {
  this.processed = processed;
 }
 
 
 
}

so if we pass it in without the id set, we'll get a BindingException.

with Spring 3.2 WebMVC, here's how we can catch all the BindingExceptions across the application and return them.  we want to return a 400 status code and the ObjectErrors in the body of the response.

package de.incompleteco.spring.web.controller;

import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import de.incompleteco.spring.web.domain.ErrorEntity;

@ControllerAdvice
public class ValidatorAdvice {

 @ExceptionHandler(BindException.class)
 public @ResponseBody ErrorEntity handleException(BindException exception,HttpServletResponse response) {
  ErrorEntity entity = new ErrorEntity();
  entity.setErrors(exception.getBindingResult().getAllErrors());
  //set the response status
  response.setStatus(HttpStatus.BAD_REQUEST.value());
  //return
  return entity;
  
 }
 
}


and here's our "payload" entity

package de.incompleteco.spring.web.domain;

import java.io.Serializable;
import java.util.List;

import org.springframework.validation.ObjectError;

public class ErrorEntity implements Serializable {

 private List< ObjectError > errors;

 public List< ObjectError > getErrors() {
  return errors;
 }

 public void setErrors(List< ObjectError > errors) {
  this.errors = errors;
 }
 
}


what our result will be is the ErrorEntity processed via Jackson (JSON) and the status code set to 400.

by the way, our Spring configuration is this;




 
 
 



don't ya love 3.2!

2 comments:

  1. Since 3.2.2: Seems like it changed from BindException to MethodArgumentNotValidException

    ReplyDelete
    Replies
    1. yes - there seems to be a few more 'significant' changes in the 3.2.2+ releases

      Delete