添加依赖
1 <!-- 参数校验 -->
2 <dependency>
3 <groupId>org.hibernate.validator</groupId>
4 <artifactId>hibernate-validator</artifactId>
5 <version>6.0.17.Final</version>
6 </dependency>
一.基本类型参数(String)校验
1.注解要写在接口中,实现类会自动继承,如果实现类的某个重写方法没有加上了注解,接口中却没有定义,运行时会产生redefine异常
接口:
User getUserById(@NotNull(message = "uid不能为null") @Min(value = 1,message = "uid不合法") Integer id);
实现类:
@Override
public User getUserById(@NotNull(message = "uid不能为null") @Min(value = 1,message = "uid不合法") Integer id) {
return userMapper.getUserById(id);
}
Controller(restful风格最容易出的问题就是参数为空,只要不传就是404,不要尝试:xxx/getUserById/null,这种写法是400,给url设置null没有意义,解决方式很简单,给Controller增加一个映射路径即可,空参数导致的bind异常可以用在全局异常处理器捕获即可,当然你在web.xml中统一处理404也可以):
1 @RequestMapping(value = {"/getUserById/{uid}","/getUserById"})
2 public @ResponseBody Object getUserById(@PathVariable Integer uid) {
3 return userService.getUserById(uid);
4 }
2.提供校验器,自定义异常(可选),全局异常处理器
校验器:
1 public class ParamsValidator { 2 3 public static ExecutableValidator getValidator() { 4 ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); 5 return validatorFactory.getValidator().forExecutables(); 6 } 7 }
自定义异常(可选):
1 public class ValidParamException extends RuntimeException { 2 private static final long serialVersionUID = 1L; 3 4 private Set<ConstraintViolation<Object>> validateResult; 5 6 public ValidParamException() { 7 super(); 8 } 9 10 public ValidParamException(String message) { 11 super(message); 12 } 13 14 public ValidParamException(Set<ConstraintViolation<Object>> validateResult) { 15 this.validateResult = validateResult; 16 } 17 18 public Set<ConstraintViolation<Object>> getValidateResult() { 19 return validateResult; 20 } 21 22 public void setValidateResult(Set<ConstraintViolation<Object>> validateResult) { 23 this.validateResult = validateResult; 24 } 25 26 @Override 27 public String getMessage() { 28 return validateResult.iterator().next().getMessage(); 29 } 30 31 public Map<Object, String> getErrorMap() { 32 Map<Object, String> map = new HashMap<Object, String>(6); 33 Iterator<ConstraintViolation<Object>> iterator = validateResult.iterator(); 34 while (iterator.hasNext()) { 35 ConstraintViolation<Object> cons = iterator.next(); 36 Path propertyPath = cons.getPropertyPath(); 37 String message = cons.getMessage(); 38 map.put(propertyPath, message); 39 } 40 return map; 41 } 42 43 }
全局异常处理器:
1 @ControllerAdvice
2 @EnableWebMvc
3 public class GlobalExceptionHandler {
4 // 单参数校验
5 @ExceptionHandler(ValidParamException.class)
6 public @ResponseBody Map<Object, String> validParamException(HttpServletRequest req, ValidParamException vpe) {
7 return vpe.getErrorMap();
8 }
9
10 // 对象类型参数校验
11 @ExceptionHandler(MethodArgumentNotValidException.class)
12 public @ResponseBody Map<Object, String> methodArgumentNotValidException(MethodArgumentNotValidException ex) {
13 String parameterName = ex.getParameter().getParameterName();
14 Map<Object, String> map = new HashMap<Object, String>(6);
15 map.put(parameterName, ex.getLocalizedMessage());
16 return map;
17 }
18
19 // pathvariable不传递参数时抛出的异常
20 @ExceptionHandler(ServletRequestBindingException.class)
21 public @ResponseBody Map<Object, String> servletRequestBindingException(ServletRequestBindingException ex) {
22 Map<Object, String> map = new HashMap<Object, String>(6);
23 map.put("error", ex.getLocalizedMessage());
24 return map;
25 }
26 }
3.使用aop进行拦截
关于aop"失效"的问题有几点说明:
1)如果拦截controller,那么aop的配置要写在springmvc的配置文件中,拦截其他层(如service)写在spring的配置文件中
2)被拦截的类必须也被spring管理否则无法拦截成功
3)开启注解扫描时,springmvc只扫描@Controller类型的注解,其他的如@Service,@Repository注解由spring进行扫描
aop:
1 @Component 2 @Aspect 3 public class UserAspect { 4 5 private ExecutableValidator validator = ParamsValidator.getValidator(); 6 7 @Pointcut("execution (* cn.tele.service.*.*(..))") 8 private void pt() { 9 } 10 11 @Before("pt()") 12 public void checkParams(JoinPoint jp) { 13 14 Object target = jp.getTarget(); 15 Object[] params = jp.getArgs(); 16 MethodSignature methodSignature = (MethodSignature) jp.getSignature(); 17 18 String[] paramNames = methodSignature.getParameterNames(); 19 Method method = methodSignature.getMethod(); 20 21 Set<ConstraintViolation<Object>> validateResult = validator.validateParameters(target, method, params); 22 if (!validateResult.isEmpty()) { 23 throw new ValidParamException(validateResult); 24 } 25 } 26 27 }
4.测试结果:
1)传入-1
2)不传
可以在aop中打印日志啥的
二.对象类型参数校验
1.在javaBean中添加注解,对一些特殊字段进行分组,如id,插入数据时,不需要校验可以为null,而查询,删除,更新等操作必须校验
1 @NotNull(message = "uid不能为null",groups = {Query.class,Update.class,Delete.class})
2 @Min(value = 1,message = "uid不合法")
3 private Integer uid;
4
5 @NotBlank(message = "姓名不能为空")
6 @Size(max = 20,message = "姓名最大长度为50个字符")
7 private String userName;
8
9 @NotBlank(message = "性别不能为空")
10 @Size(max = 20,message = "性别最大长度位20个字符")
11 private String sex;
12
13 @NotNull
14 @Max(value = 70,message = "最大年龄为70岁")
15 private Integer age;
16
17 @NotNull
18 private Integer departmentId;
19
20 @Value(value = "1")
21 private Integer state;
分组只是个标记,用接口定义就好
1 public interface Query {
2
3 }
2.在参数前添加@Valited注解,该注解支持分组,@Valid不支持,如果你选择的校验位置与上面定义的aop拦截的位置相同,那就会出问题了,
你的代码会走aop的逻辑然后去用你校验单个参数的校验器去进行校验,这样无法校验出问题,因此推荐放在controller层
1 @RequestMapping("/insertUser")
2 public @ResponseBody String insertUser(@Validated @RequestBody User user) {
3 Integer count = userService.insertUser(user);
4 return count ==1 ? "成功增加1条记录" : "增加" + user + "失败";
5 }
校验指定分组
3.抛出的异常会走上面贴出的全局异常处理器的代码
4.测试
1)正常情况,注意没有id
2)丢失userName