在最开始的时候,web项目中接口的第一步几乎都是判断参数的合法性,例如值是否为null等等。因为有guava的工具包,刚开始手写校验参数还OK,例如:
if (StringUtils.isBlank(name)) { return "name不能为空"; }
随着项目功能接口变多,这种写法越来越麻烦;hibernate出了一种注解,可以很方便地在实体类属性上标注来校验合法性,特别是当springboot出现以后,两者结合完美无缺,也方便返回信息的国际化。
这种注解写法例如:
@NotEmpty(message = "请求不合法,name不能为空") private String name;
然后在接口参数中用@Valid注解标注上面的领域模型,然后参数合法性校验有一个绑定结果叫BindingResult,要想知道是否有错误你还得这么做:
@RequestMapping("/sender/message") @ResponseBody public ResponseBean sendMessage(@Valid Message messageBean, BindingResult bindingResult){ ...... if (bindingResult.hasErrors()) { System.out.println("验证失败!"); } ...... }
显然每个接口都这么做不够优雅,一般都是在一个针对所有controller的增强全局异常 拦截器中处理通用异常,例如:
@ControllerAdvice public class ValidateExceptionHandler { @ResponseBody @ExceptionHandler(BindException.class) public Result handleBindException(BindException e) { BindingResult bindingResult = e.getBindingResult(); return Result.fail(Code.ILLIGAL_PARAM_ERROR, bindingResult.getFieldError().getDefaultMessage()); }
不过你要注意了,就拿@NotBlank这个注解来说,
javax.validation.constraints.NotBlank和org.hibernate.validator.constraints.NotBlank 这两个包里都有这个注解,他们的校验不通过所抛出的异常也不一样,并且与项目中集成的spring版本也有关系,例如hibernate包中的@NotBlank注解与spring4.0版本及4.0以下版本结合使用,所抛出的异常为:
@ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public ResponseResultVO handleMethodArgumentNotValidException( MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); return ResponseResultVO.fail(Code.ILLIGAL_PARAM_ERROR, bindingResult.getFieldError().getDefaultMessage()); }
最后,在@Valid注解使用过程中发现@Valid只能作用在参数领域模型上,无法作用在单个属性上;要想作用在单个属性上需要结合spring的@Validate注解与@Min、@Max、@NotBlank等针对单个属性的注解使用,并且@Validate只能加在当前controller类上面,用法例如:
@Validated @RequestMapping("/user") @Controller public class UserController { @ResponseBody @RequestMapping("/head") public Result getHeadThumb(@NotNull(message = "请求不合法,userId不能为空") @Min(value = 0, message = "userId不合法") Long userId, HttpServletResponse response) { .... } }
@Validate注解在这个包里 org.springframework.validation.annotation.Validated ;而且这种抛出的异常类型为:
@ResponseBody @ExceptionHandler(ConstraintViolationException.class) public Result handleConstraintException(ConstraintViolationException ex) { return Result.fail(Code.ILLIGAL_PARAM_ERROR, ex.getMessage().split(":")[1]); }