在web开中,参数校验也是不可或缺的一部分,在SpringBoot中,准确说是SpringMVC中参数校验有多种方式,下面一一讲解各个方法是如何校验参数的。

直接校验

这种方法简单粗暴,直接在控制器方法中进行校验,不使用框架内置的注解。

@Controller
public class HelloController {

    @ResponseBody
    @GetMapping("/testValidate")
    public String getUrlValue(String name, String gender) {
        if (name == null || name.isEmpty()) {
            return "名字不能空";
        }
        if (gender == null || gender.isEmpty()) {
            return "性别不能空";
        }
        return "hello";
    }

}

浏览器输入地址http://localhost:8080/testValidate?name=yxjc123,测试一下。

SpringBoot参数校验的几种方式

这种方法对于1,2个参数的校验倒是还好,如果过多不建议这样,所以推荐使用下面介绍的方法进行校验。

添加校验支持

在pom.xml文件添加校验支持spring-boot-starter-validation。

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-validation</artifactId> 
</dependency>
@Null所注解的元素值为null
@NotNull所注解的元素值不能为null
@NotBlank所注解的元素值有内容,仅包含空格、制表符(tab键)的字符串视为空
@Size所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
@AssertFalse所注解的元素必须是Boolean类型,且值为false
@AssertTrue所注解的元素必须是Boolean类型,且值为true
@Max所注解的元素必须是数字,且值小于等于给定的值
@Min所注解的元素必须是数字,且值大于等于给定的值
@DecimalMax所注解的元素必须是数字,且值小于等于给定的值,通常用于BigDecimal类型
@DecimalMin所注解的元素必须是数字,且值大于等于给定的值,通常用于BigDecimal类型
@Digits所注解的元素必须是数字,且值必须是指定的位数
@Future所注解的元素必须是将来某个日期
@Range所注解的元素需在指定范围区间内
@Past所注解的元素必须是某个过去的日期
@PastOrPresent所注解的元素必须是过去某个或现在日期
@Pattern所注解的元素必须满足给定的正则表达式
@Email所注解的元素需满足Email格式

以上是常用的校验方法。

单个参数注解校验

在SpringBoot中,Controller层可以使用这些注解对单个参数进行校验,此方法适用于参数较少的情况下。

@Controller
@Validated
public class HelloController {

    @ResponseBody
    @GetMapping("/testValidate")
    public String getUrlValue(@NotNull(message = "名字不能空") String name,
                              @NotNull(message = "性别不能空") String gender) {

        return "hello";
    }

}
上面的例子中需要对HelloController添加注解@Validate 否则无效,浏览器中输入http://localhost:8080/testValidate?name=yxjc123,报错信息如下:

SpringBoot参数校验的几种方式

后台的报错信息:

SpringBoot参数校验的几种方式

这种方式的返回结果对用户或者前端接口调用不是非常友好,需要对这些异常信息进行统一处理,请看后面的章节,SpringBoot统一异常处理

对于参数较多的时候,这显然是不合理的,我们会在Controller层的方法中堆砌大量的校验代码,接下来介绍实体类的校验。

实体类的校验

实体类校验是将校验的方法写在实体中,控制器方法用于接受参数。

控制器代码

@Controller
@Validated
public class HelloController {

    @ResponseBody
    @PostMapping("/testValidate")
    public String getUrlValue(@Validated User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return bindingResult.getFieldError().getDefaultMessage();
        }
        return "hello";
    }

}
User.java
package com.example.yxjc.domain;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 2733191978271622951L;

    @NotNull(message = "名字不能空")
    private String name;
    @NotNull(message = "性别不能空")
    @Range(min=0, max=2, message = "性别输入错误")
    private String gender;

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

使用postman测试

SpringBoot参数校验的几种方式

上面的例子中如果每个方法都是用BindingResult对参数进行错误判断也是不可取的,因为这样也增加了代码的重复度,所以同样可以使用统一异常处理。 

分组校验

一般情况下系统都有注册和登录功能,

在系统注册的时候需要提交用户名、密码、性别、手机号等字段,

在登录的时候只需要用户名和密码字段,

如果在没有分组校验的情况下需要写两个实体类比如:

UserLogin.class和UserReg.class,显然这一增加了系统的一些冗余代码,为了解决这个问题分组校验出现了。

它可以在一个实体类中定义两个不同的分组A和B,A分组用于登录,B分组用于注册。以下是例子:

1) 首先定义两个分组接口GroupA.class和GroupB.class

package com.example.yxjc.domain;
//登录
public interface GroupA {
}
package com.example.yxjc.domain;
//注册
public interface GroupB {
} 
2) 给实体类增加不同的分组
package com.example.yxjc.domain;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 2733191978271622951L;

    @NotNull(message = "名字不能空",groups = {GroupA.class, GroupB.class})
    private String name;
    @NotNull(message = "密码不能空",groups = {GroupA.class, GroupB.class})
    private String passwd;
    @NotNull(message = "手机号不能空",groups = {GroupB.class})
    private String mobile;
    @NotNull(message = "性别不能空",groups = {GroupB.class})
    @Range(min=0, max=2, message = "性别输入错误")
    private String gender;

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
} 
3) 控制器层定义两个不同的方法login和register
@Controller
@Validated
public class HelloController {

    @ResponseBody
    @PostMapping("/user/login")
    public String login(@Validated({GroupA.class}) User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return bindingResult.getFieldError().getDefaultMessage();
        }
        return "login";
    }

    @ResponseBody
    @PostMapping("/user/register")
    public String register(@Validated({GroupB.class})  User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return bindingResult.getFieldError().getDefaultMessage();
        }
        return "register";
    }

}
4) 使用postman测试一下

SpringBoot参数校验的几种方式

SpringBoot参数校验的几种方式

虽然两个方法使用了同一个类user.class但是,因为有分组校验的区别,所以在校验的时候它们的字段是不同的。其中

登录方法使用字段 name和passwd

注册方法使用字段 name、passwd、Mobile和gender。

自定义校验方法

虽然框架中提供的注解校验的方法已经很多了,但是有时候我们需要自定义一些通用的校验方法,比如身份证号码,这是框架中所没有的。下面介绍详细的处理过程。

1)首先,定义一个@IdCard注解

package com.example.yxjc.validate;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidate.class)
public @interface IdCard {
    String message() default "身份证格式错误";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
} 

2) 定义真正的身份证校验类  IdCardValidate.class,关于身份证的判断,由于篇幅问题这里简单处理,只判断是否等于12345。

package com.example.yxjc.validate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class IdCardValidate implements ConstraintValidator<IdCard, Object> {

    @Override
    public boolean isValid(Object idCard, ConstraintValidatorContext constraintValidatorContext) {
        //关于身份证的判断,由于篇幅问题这里简单处理,只判断是否等于12345。
        return idCard.toString().equals("12345");
    }

} 
3) 修改实体类
package com.example.yxjc.domain;
import com.example.yxjc.validate.IdCard;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 2733191978271622951L;

    @NotNull(message = "名字不能空")
    private String name;
    @Range(min=0, max=2, message = "性别输入错误")
    private String gender;
    @IdCard //身份证校验
    private String idCard;


    public String getName() {
        return name;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }


} 
4) 修改我们的控制器类
@Controller
@Validated
public class HelloController {

    @ResponseBody
    @PostMapping("/user/idCard")
    public String idCard(@Validated User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return bindingResult.getFieldError().getDefaultMessage();
        }
        return "success";
    }


}
5) 使用postman测试一下

SpringBoot参数校验的几种方式