前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC参数校验

SpringMVC参数校验

作者头像
晚上没宵夜
发布2020-03-10 14:41:10
1K0
发布2020-03-10 14:41:10
举报

SpringMVC是根据参数的名字,然后用setter方法来对数据进行绑定的,若类型没有匹配上则会出现400的错误,同时还要注意空值问题

1. 参数校验

我们在做Web层的时候,接收了各种参数,尽管前端已经做了验证,但难免恶意传参,所以要对传过来的数据保持不信任的态度来进行参数校验

笔者日常进行验证的方式如下:

代码语言:javascript
复制
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(String name, String email) {
     
    if(name == null || name.isEmpty()){
        return "名字不能为空";
    }
    if(email == null || email.isEmpty()){
        
        // 这里还要加上邮箱格式的验证,省略省略
        
        return "邮箱不能为空";
    }  
}

乍一看好像没什么问题,能够应付需求,但是一旦参数多了起来就会像下面那样

代码语言:javascript
复制
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(String name, String email, String sex, String password, String nickName, String address) {
     
    if(name == null || name.isEmpty()){
        return "名字不能为空";
    }
    if(email == null || email.isEmpty()){
        return "邮箱不能为空";
    }  
    if(sex == null || sex.isEmpty()){
        return "性别不能为空";
    }
    if(password == null || password.isEmpty()){
        return "密码不能为空";
    }
    if(address == null || address.isEmpty()){
        return "地址不能为空";
    }  
}

这里看还挺整齐的,一目了然,其实除了非空判断还需各种格式验证没有列出了,如果再添加参数就成了累赘,一个类中参数校验的代码就占了大部分,得不偿失

这时候就该考虑简便的参数校验方式了——JSR-303(基于注解)

2. JSR-303

JSR-303是一个被提出来的数据验证规范,所以这仅仅是个接口,没有具体实现的功能,容易被误解为JSR-303就是用于数据验证的的工具。我们要用到JSR-303的规范,那么就需要导入实现类的jar包,比如Hibernate Validator也是我们后面使用的jar包。

Spring也提供了参数校验的方式,即实现其内部的validator接口来进行参数校验,接口有两个方法:

代码语言:javascript
复制
public class UserValidator implements Validator {

    // 判断是否支持验证该类
    public boolean supports(Class clazz) {
        return User.class.equals(clazz);
    }

    // 校验数据,将报错信息放入Error对象中
    public void validate(Object obj, Errors e) {
        // ValidationUtils的静态方法rejectIfEmpty(),对属性进行非空判断
        ValidationUtils.rejectIfEmpty(e,"name","name.empty");
        User user = (User)obj;
        if(user.getAge() < 0){
            e.rejectValue("age", "年龄不能为负数");
        }
    }
}

我们当然不满足那么麻烦的方法,所以JSR-303出场

JSR-303是基于注解校验的,注解已经实现了各种限制,我们可以将注解标记在需要校验的类的属性上,或是对应的setter方法上(笔者习惯标记在属性上)

导入Hibernate Validator依赖jar包,笔者使用maven工程

代码语言:javascript
复制
<!--    参数校验    -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.2.Final</version>
</dependency>

hibernate-validator实现了JSR-303的所有功能,额外还提供了一些实用的注解。我们可以将其分成两部分,一个是JSR-303规范中包含的,另一部分是hibernate额外提供的。下面的注解看解释就能明白是什么功能了

JSR-303规范

Annotation

Description

@Null

被注释的元素必须为 null

@NotNull

被注释的元素必须不为 null

@AssertTrue

被注释的元素必须为 true

@AssertFalse

被注释的元素必须为 false

@Min(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max, min)

被注释的元素的大小必须在指定的范围内

@Digits (integer, fraction)

被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past

被注释的元素必须是一个过去的日期

@Future

被注释的元素必须是一个将来的日期

@Pattern(value)

被注释的元素必须符合指定的正则表达式

hibernate额外提供的

Constraint

详细信息

@Email

被注释的元素必须是电子邮箱地址

@Length

被注释的字符串的大小必须在指定的范围内

@NotEmpty

被注释的字符串的必须非空

@Range

被注释的元素必须在合适的范围内

3. JSR-303的简单使用

3.1 在需要校验的属性上标记注解

注解有个属性message存放自定义的错误信息

代码语言:javascript
复制
public class User {

    @NotNull(message = "名字不能为空")
    private String name;

    @Email(message = "邮箱格式错误")
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    // 各种getter / setter / 构造器
}

3.2 开启校验

在Controller方法入参中需要校验的参数前加入@Validated()表明需要校验,后方要加@BindingResult接收错误信息,若没加即接收不了错误信息会报错(若使用了全局异常处理则可以不加)。@Validated()和@BindingResult二者一前一后紧密相连的,中间不能有任何数值相隔。

代码语言:javascript
复制
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(@Validated() User user, BindingResult bindingResult) {

    // 判断是否有错
    if (bindingResult.hasErrors()) {
        // 获取字段上的错误
        FieldError errors = bindingResult.getFieldError();
        // 输出message信息
        return (errors.getDefaultMessage() + "\n");
    }
    // dosomething
}

3.3 补充

按上面的方法日常使用应该没什么问题了,数据校验中还有分组自定义校验的知识点,这里笔者就不做 (tou) 说明 (lan) 了

4. 笔者遇到的小插曲

我们知道前端传参过来都是字符串,经过Spring的类型转换器转换成为我们需要的类型才能正常使用,之前笔者没有使用JSR-303规范来校验参数的时候莫得发觉问题,但这也为现在埋下了坑

如果传个整型呢?

代码语言:javascript
复制
public class User {

    @Min(value = 0, message = "不能为负数")
    private int id;
    
    // 各种getter / setter / 构造器
}
代码语言:javascript
复制
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listByPage(@Validated() User user, BindingResult bindingResult) {

    if (bindingResult.hasErrors()) {
        FieldError errors = bindingResult.getFieldError();
        return (errors.getDefaultMessage() + "\n");
     }
    
    // dosomething
}

乍一看没有什么问题,普通使用能过去。但是但是但是 int id 传了空值就会报错:

代码语言:javascript
复制
Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'id'; nested exception is java.lang.NumberFormatException: For input string: ""

// 翻译:转换String到int id失败,报错原因是数字格式化异常,因为输入了字符串 “”

这里就是那个小小小的插曲,开始真是不知如何解决

解决方法

使用包装类Integer,类型对不上就不匹配了,包装类还会自动装箱和拆箱,所以很方便解决空值问题

代码语言:javascript
复制
// Integer id

// 替换成包装类之后传的参数为,空值不接收即为null
User{id=null, name='jiafu liu', email='1210911104@qq.com'}

教训是:对于可能会传空值的属性一般会用包装类型

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 参数校验
  • 2. JSR-303
  • 3. JSR-303的简单使用
    • 3.1 在需要校验的属性上标记注解
      • 3.2 开启校验
        • 3.3 补充
        • 4. 笔者遇到的小插曲
          • 解决方法
            • 教训是:对于可能会传空值的属性一般会用包装类型
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档