SpringMVC参数校验一般使用hibernate-validator校验框架,网上相关注解资料很多,本文不做介绍,本文主要介绍使用如何降低校验编程的复杂度。
方案一:@Valid + BindingResult
使用@Valid + BindingResult进行参数校验,网上大部分参数校验文章都采用这种方式,示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
   |  @Data public class DeviceStorageRecordVO {     @NotNull(message = "数量不能为空")     private Integer count; }
 
  @RestController @RequestMapping("device") public class DeviceCtrl {          @PostMapping("storageRecord/add")     public JsonResult addStorageRecord(@Valid @RequestBody DeviceStorageRecordVO deviceStorageRecordVO, BindingResult result) {     	     	StringBuilder errorBuilder = new StringBuilder();         List<ObjectError> errors = result.getAllErrors();
          for(ObjectError error : errors){             if(StringUtils.hasText(errorBuilder)) {                 errorBuilder.append(" ");             }             errorBuilder.append(error.getDefaultMessage());         }
          if(StringUtils.hasText(errorBuilder.toString())){             throw new BusiException(ResultEnum.PARAMS_ILLEGAL.getCode(),errorBuilder.toString());         }                  deviceServ.addStorageRecord(deviceStorageRecordVO);         return MVCUtil.success();     } }
 
  @Aspect @Component @Slf4j public class WebAspect {
  	 	 	      	private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();     private void checkParams(ProceedingJoinPoint joinPoint) {         Object[] args = joinPoint.getArgs();         MethodSignature signature = (MethodSignature) joinPoint.getSignature();         Method method = signature.getMethod();         Parameter[] ps = method.getParameters();         StringBuilder errorBuilder = new StringBuilder();         for (int i = 0; i < ps.length; i++) {             Parameter p = ps[i];             Annotation annotation = p.getAnnotation(Valid.class);                          if (annotation != null) {                 Set violations = validator.validate(args[i]);                 for (Object violation : violations) {                     ConstraintViolation constraintViolation = (ConstraintViolation)violation;                     if(StringUtils.isNotBlank(errorBuilder)) {                         errorBuilder.append(" ");                     }                     errorBuilder.append(constraintViolation.getMessage());                 }             }         }         if(StringUtils.isNotBlank(errorBuilder.toString())){             throw new BusiException(ResultEnum.PARAMS_ILLEGAL.getCode(),errorBuilder.toString());         }     } }
 
  | 
 
在方法1处处理result值,可进行封装 或 如方法2一样使用AOP统一处理。
缺点:麻烦,遗忘添加@Valid 或 BindingResult未跟在@Valid标记的Bean后面均会导致校验无效或应用报错(MethodArgumentNotValidException异常)。
方案二:使用@Valid,不使用BindingResult。
当使用@Valid而未添加BindingResult进行参数校验时,应用会报MethodArgumentNotValidException异常,此时使用ControllerAdvice进行异常的统一处理即可,在方法里进行错误参数message的组装返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | @ControllerAdvice(annotations = {RestController.class, Controller.class}) @ResponseBody @Slf4j public class ExceptionHandler { 	@ExceptionHandler(MethodArgumentNotValidException.class)     public JsonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {         BindingResult result = e.getBindingResult();         StringBuilder errorBuilder = new StringBuilder();         List<ObjectError> errors = result.getAllErrors();
          for(ObjectError error : errors) {             if(StringUtils.hasText(errorBuilder)) {                 errorBuilder.append(" ");             }             errorBuilder.append(error.getDefaultMessage());         }
          if(StringUtils.hasText(errorBuilder.toString())){             return MVCUtil.error(ResultEnum.PARAMS_ILLEGAL, errorBuilder.toString());         }         return MVCUtil.error(ResultEnum.PARAMS_ILLEGAL, "参数非法");     } }
  | 
 
优点:不需要添加BindingResult,错误统一处理,代码复杂度降低。
方案三:不使用@Valid和BindingResult
不使用@Valid和BindingResult而在AOP方法里手动进行校验。去掉@Valid注解及BindingResult参数并使用方案一中的AOP,去掉其中的@Valid注解判断(使得所有参数都进行校验)即可。
优点:代码复杂度进一步降低,只需要在需要校验的Bean中使用校验注解进行标记即可。
方案三代码量及复杂度最低,但由于历史原因项目使用了@Valid,担心大量修改导致系统出现一些故障,故项目采用方案二、方案三同时使用,老代码不做修改,新代码直接使用方案三。