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,担心大量修改导致系统出现一些故障,故项目采用方案二、方案三同时使用,老代码不做修改,新代码直接使用方案三。