声明:本文属原创文章,始发于公号:程序员自学之道,同步发布到 sf,转载请注明出处。java
在 Web 开发中, 咱们常常须要校验各类参数,这是一件繁琐又重要的事情,对于不少人来讲,在作参数校验的时候,会有如下几种类型的处理方式:git
校验太麻烦了,让客户端去负责校验就好了,调用方传错了是调用方的问题,不是服务的问题,甩个 500 错误让他们好好检讨:程序员
有多少参数,我就写多少个 if 语句作判断,校验不经过的都写一句友好的提示,如:github
本身写个参数校验的通用工具,而后每一个请求接收到的参数都调用工具方法来校验,校验不经过就把校验结果返回给调用方。这样确实能减小不少冗余的代码:ajax
对 SpringMVC 了解比较全面的朋友都知道,它支持 Bean Validation,所以能够经过使用 javax.validation.constraints
包下的注解,如 @NotNull @Max @Min
等,来实现由框架处理数据校验:
首先,添加 hibernate-validator 依赖(SpringBoot 项目为咱们自动添加了):spring
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.10.Final</version> </dependency>
而后,在参数对象的字段上打注解:
最后,在 Controller 中给参数对象添加 @Valid 注解,并处理校验结果:json
这样作,每一个Controller方法进来都要处理结果,也都是冗余的代码。浏览器
以上这些处理方式都有不足之处:框架
那么,有没有更好的解决方案呢?答案是:有的!工具
其实,上面的半自动型的解决方式,只要再进一步,就能够实现全自动了!
想一想,若是上面的半自动型例子中,咱们不在 Controller 方法中处理校验结果,会怎么样呢?答案是,会抛出异常:
那么,若是咱们作了全局统一异常处理,不就能够实现自动校验并返回咱们想要的结果了吗?因此咱们能够这样作:
@ControllerAdvice public class GlobalExceptionHandler { /** 统一处理参数校验异常 */ @ExceptionHandler @ResponseBody public ResultBean<?> handleValidationException(BindException e) { // 获取 String msg = e.getBindingResult().getAllErrors().stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(",")); log.warn("参数校验不经过, msg: {}", msg); return ResultBean.fail(msg); } }
然而,若是你只统一处理 BindException 这个异常的话,你会发现这个方案有时候好用,有时候却会“失灵”。为何呢?由于对于不一样的参数解析方式,Spring作参数校验时会抛出不一样的异常,并且这些异常没有继承关系,经过异常获取校验结果的方式也各不相同(好坑爹~)。
总结起来有如下几种异常须要处理:
必填参数没传: ServletRequestBindingException
因此完整的处理方法应该是这样:
@ExceptionHandler({ConstraintViolationException.class, MethodArgumentNotValidException.class, ServletRequestBindingException.class, BindException.class}) @ResponseBody public ResultBean<?> handleValidationException(Exception e) { String msg = ""; if (e instanceof MethodArgumentNotValidException) { MethodArgumentNotValidException t = (MethodArgumentNotValidException) e; msg = getBindingResultMsg(t.getBindingResult()); } else if (e instanceof BindException) { BindException t = (BindException) e; msg = getBindingResultMsg(t.getBindingResult()); } else if (e instanceof ConstraintViolationException) { ConstraintViolationException t = (ConstraintViolationException) e; msg = t.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining(",")); } else if (e instanceof MissingServletRequestParameterException) { MissingServletRequestParameterException t = (MissingServletRequestParameterException) e; msg = t.getParameterName() + " 不能为空"; } else if (e instanceof MissingPathVariableException) { MissingPathVariableException t = (MissingPathVariableException) e; msg = t.getVariableName() + " 不能为空"; } else { msg = "必填参数缺失"; } log.warn("参数校验不经过,msg: {}", msg); return ResultBean.fail(msg); }
添加了这个全局异常处理器以后,就能够自动参数校验了!体验飞升的感受~~
完整实例已经上传到 GitHub,请查看:https://github.com/dadiyang/s...
若是我是在浏览器上访问的,如发一个访问某个页面,结果参数校验不经过,这时这个统一异常处理器会返回一个 json 格式的文本。普通用户看到这样的文本,估计要懵圈了。
那么问题来了,怎么能让打开页面的请求返回错误页面,而 ajax 请求返回 json 呢?
个人实例代码已经展现了,有兴趣能够了解一下,敬请期待下一篇的讲解。