我是怎么进行SpringMVC参数校验的?

栏目: Java · 发布时间: 4年前

内容简介:在 Web 开发中, 我们经常需要校验各种参数,这是一件繁琐又重要的事情,对于很多人来说,在做参数校验的时候,会有以下几种类型的处理方式。校验太麻烦了,让客户端去负责校验就行了,调用方传错了是调用方的问题,不是服务的问题,甩个 500 错误让他们好好反省:

不要为了读文章而读文章,一定要带着问题来读文章,勤思考。

在 Web 开发中, 我们经常需要校验各种参数,这是一件繁琐又重要的事情,对于很多人来说,在做参数校验的时候,会有以下几种类型的处理方式。

甩锅型

校验太麻烦了,让客户端去负责校验就行了,调用方传错了是调用方的问题,不是服务的问题,甩个 500 错误让他们好好反省:

我是怎么进行SpringMVC参数校验的?

劳模型

有多少参数,我就写多少个 if 语句做判断,校验不通过的都写一句友好的提示,如:

我是怎么进行SpringMVC参数校验的?

工具型

自己写个参数校验的通用工具,然后每个请求接收到的参数都调用 工具 方法来校验,校验不通过就把校验结果返回给调用方:

我是怎么进行SpringMVC参数校验的?

半自动型

对 SpringMVC 了解比较全面的朋友都知道,它支持 Bean Validation,因此可以通过使用 javax.validation.constraints 包下的注解,如 @NotNull@Max@Min 等,来实现由框架处理数据校验。

首先,添加 hibernate-validator 依赖(SpringBoot 已经为我们自动添加了)。

<dependency>
    
<groupId>
org.hibernate.validator
</groupId>
    
<artifactId>
hibernate-validator
</artifactId>
    
<version>
6.0.10.Final
</version>
</dependency>复制代码

然后,在参数对象的字段上打注解:

我是怎么进行SpringMVC参数校验的?

最后,在 Controller 中给参数对象添加 @Valid 注解,并处理校验结果:

Tip:如果你的参数不是对象,一定要在 Controller 上打 @Validated 注解!

我是怎么进行SpringMVC参数校验的?

这样做,每个 Controller 方法都要处理结果,也是很麻烦。

方案分析

以上这些处理方式都有不足之处:

首先,参数校验是一件非常重要的事,客户端要把住第一道防线,而服务方要采取不信任的态度,做好参数校验。否则非法请求参数小则影响用户体验或者产生垃圾数据,大则会拖跨整个系统!

其次,手工对所有的参数进行校验相当繁琐,容易出错,而且 So boring~

最后,通过工具来完成是比较好的方式,但是必须让工具变得优雅一些。

那么,有没有更好的解决方案呢?答案是:有的!

最佳实践

其实,上面的半自动型的解决方式,只要再进一步,就可以实现全自动了!

想想,如果上面的半自动型例子中,我们不在 Controller 方法中处理校验结果,会怎么样呢?答案是,会抛出异常:

我是怎么进行SpringMVC参数校验的?

那么,如果我们做了全局统一异常处理,不就可以实现自动校验并返回我们想要的结果了吗?所以我们可以这样做:

@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做参数校验时会抛出不同的异常,而且这些异常没有继承关系,通过异常获取校验结果的方式也各不相同(好坑爹~)。

总结起来有以下几种异常需要处理:

对象参数接收请求体,即 RequestBody:

MethodArgumentNotValidException

请求参数绑定到对象参数上:

BindException

普通参数:

ConstraintViolationException
必填参数缺失:
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 = t.getBindingResult().getAllErrors().stream()
               .map(
DefaultMessageSourceResolvable
::getDefaultMessage)
               .collect(
Collectors
.joining(
","
));
    } else if (e instanceof 
BindException
) {
        
BindException
 t = (
BindException
) e;
        msg = t.getBindingResult().getAllErrors().stream()
               .map(
DefaultMessageSourceResolvable
::getDefaultMessage)
               .collect(
Collectors
.joining(
","
));
    } 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);
}复制代码

添加了这个全局异常处理器之后,就可以自动参数校验了!体验飞升的感觉~~

但是,如果用户是在浏览器上访问某个页面,然后参数校验不通过时这个统一异常处理器会返回一个 json 格式的文本。普通用户看到这样的文本,估计要懵圈了。

那么问题来了,怎么才能让打开页面的请求返回错误页面,而 ajax 请求返回 json 呢?我的实例代码已经展示了,有兴趣可以了解一下。

---------------------

版权声明:本文为博主原创文章,转载请附上博文链接!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Understanding Machine Learning

Understanding Machine Learning

Shai Shalev-Shwartz、Shai Ben-David / Cambridge University Press / 2014 / USD 48.51

Machine learning is one of the fastest growing areas of computer science, with far-reaching applications. The aim of this textbook is to introduce machine learning, and the algorithmic paradigms it of......一起来看看 《Understanding Machine Learning》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器