Spring boot JSR-303验证实战,简单又全面

初次使用Spring Boot里面的验证,还需要先研究一下。Spring Boot里面都有什么验证呢?Spring Boot支持JSR-303验证规范,JSR是Java Specification Requests的缩写。JSR-303是Bean Validation 1.0 (JSR 303),说白了就是基于bean的验证,更多的解释参考JCP的官网。在默认情况下,Spring Boot会引入Hibernate Validator机制来支持JSR-303验证规范。

基于JSR-303的注解有哪些,上张图,以便日后查看。更多还需参考网址:

https://www.ibm.com/developerworks/cn/java/j-lo-jsr303/index.html

Bean Validation 中的 constraint
表 1. Bean Validation 中内置的 constraint

表 2. Hibernate Validator 附加的 constraint

 

JSR-303是基于Bean的验证,那就是需要在Bean上加注解喽,本次使用的Spring Boot版本是2.1.4.RELEASE,为什么强调版本,版本不一样,有些实现细节就存在差异。下面上代码。

第一步,在bean上增加注解,进行验证;

/**
 * 实体类
 * @author 程就人生
 *
 */
public class Test {
    private String userUid; 
    //用户名不为空,使用默认提示
    @NotNull
    private String userName;    
    
    //密码进行长度和格式的验证,个性化提示
    @Size(min=6, max=15,message="密码长度必须在 6 ~ 15 字符之间!")
    @Pattern(regexp="^[a-zA-Z0-9|_]+$",message="密码必须由字母、数字、下划线组成!")
    private String userPwd;
    
    //手机号码也用个性化提示,使用正则表达式进行匹配,非空时不验证
    @Pattern(regexp="^1(3|4|5|7|8)\\d{9}$",message="手机号码格式错误!")
    private String userMobile;
    
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date userBirthday;
    
    private Byte status;    
    
    private Date updateDate;

    private String updateUser;

    private Date createDate;

    private String createUser;

    public String getUserUid() {
        return userUid;
    }

    public void setUserUid(String userUid) {
        this.userUid = userUid;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPwd() {
        return userPwd;
    }

    public void setUserPwd(String userPwd) {
        this.userPwd = userPwd;
    }

    public String getUserMobile() {
        return userMobile;
    }

    public void setUserMobile(String userMobile) {
        this.userMobile = userMobile;
    }

    public Byte getStatus() {
        return status;
    }

    public void setStatus(Byte status) {
        this.status = status;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }

    public String getUpdateUser() {
        return updateUser;
    }

    public void setUpdateUser(String updateUser) {
        this.updateUser = updateUser;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getCreateUser() {
        return createUser;
    }

    public void setCreateUser(String createUser) {
        this.createUser = createUser;
    }

    public Date getUserBirthday() {
        return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }
}

说明:注解@Size是限定字段长度的,@Pattern是匹配正则表达式的,@DateTimeFormat是用来转换前台传递过来的日期,前台传递过来的日期必须是yyyy-MM-dd格式的字符串,后台才能正确接收,这几个参数都没有做非空验证,所以允许为null。

用mybatis从数据库查询出来的日期格式的数据是long型,如:1558504462000,想把它转换成年月日的形式,就用注解@JsonFormat,转换出来的时间总是少一天,后面加上timezone = "GMT+8"就可以了。

第二步,在Controller上绑定验证

import java.util.HashMap;
import java.util.Map;

import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.bean.Test;

/**
 * 测试验证
 * @author sulliven
 * 
 */
@RestController
public class ValidatorTestController {

    /**
     * 使用 @Validated 开启对象验证
     * @param test
     * @return
     */
    @PostMapping("/validator")
    public Object validatorObject(@Validated Test test, BindingResult br){
        Map<String,Object> errorMap = new HashMap<String,Object>();
        if(br.hasErrors()){
            //对错误集合进行遍历,有的话,直接放入map集合中
            br.getFieldErrors().forEach(p->{
                errorMap.put(p.getField(), p.getDefaultMessage());
            });
        }
        //返回错误信息
        return errorMap;
    }
}


说明:BindingResult必须紧跟在@Validated的后面,特别是有多个的时候,要一对一对的排列,不能乱了顺序。
乱了顺序,验证失败时,只会在后台抛异常,而在controller方法里获取步到。

第三步,进行测试,先输入非法的数据,在输入合法的数据,测试结果OK

1.非法数据

2.合法数据

如果想自定义验证方法,不希望在Bean里面加注解,怎么做呢?

第一步,自定义验证类,实现Validator 接口

import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;


/**
 * 自定义验证类
 * @author sulliven
 */
public class TestValidator implements Validator{

    @Override
    public boolean supports(Class<?> clazz) {
                //对需要验证的类进行绑定
        return Test.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        if(target == null){
            //TODO
            return;
        }
        // 把校验信息注册到Error的实现类里,两种写法
        // ValidationUtils.rejectIfEmpty(errors,"userMobile",null,"手机号码不能为空!!");
        // 对对象进行强转
        Test test = (Test) target;

        // 手机号码的验证,不为空时的一些验证
        if(StringUtils.isEmpty(test.getUserMobile())){
            errors.rejectValue("userMobile", null, "手机号不能为空!");
        }
        //其他自定义验证
    }

}

说明:重写接口里的两个方法,先对需要进行验证的实体进行绑定,这个类实现了Validator接口,重写接口里的validate,自定义验证方法。

第二步,在Controller添加initBinder方法进行绑定,其他不变

说明:initBinder方法是参数转换之前执行,在执行具体的controller方法前。

/**
     * 验证处理,initBinder方法在参数转换之前执行(转换规则,格式化)
     * @param webDataBinder
     */
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {

        webDataBinder.addValidators(new TestValidator());

    }

第三步,进行测试,不输入手机号,测试结果OK

还有问题,在实体类上写正则表达式的时候,比如说手机号码的验证,可能有好几个类都需要进行手机号码格式的验证,每个类都写一次,也是很繁琐的,有没有更简单更公共的的方法呢?

当然有,使用注解,根据JSR-303规范,一个 constraint 通常由 annotation 和相应的 constraint validator 组成,一个annotation可以对那个多个constraint validator。先不管这么多,写一个验证手机号的@Mobile试一试吧。

第一步,编写注解类Mobile

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
/**
 * 验证手机号码的注解类
 * @author 程就人生
 * @date 2019年5月22日
 * @Description 
 *
 */
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MobileValidator.class)  //对应的验证实现类
public @interface Mobile { 
    
    //默认提示
    String message() default "手机号码格式错误!"; 

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

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

}

第二步,实现MobileValidator类

import java.util.regex.Pattern;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import com.alibaba.druid.util.StringUtils;
/**
 * 验证手机号码的实现类
 * @author 程就人生
 * @date 2019年5月22日
 * @Description 
 *
 */
public class MobileValidator implements ConstraintValidator<Mobile, String> { 

    //验证手机的正则表达式
    private String mobileReg = "^1(3|4|5|7|8)\\d{9}$";
    
    private Pattern mobilePattern = Pattern.compile(mobileReg); 

    public void initialize(Mobile mobile) {

    } 

    public boolean isValid(String value, ConstraintValidatorContext arg1) {
       //为空时,不进行验证
       if (StringUtils.isEmpty(value))

           return true;
       
       //返回匹配结果
       return mobilePattern.matcher(value).matches();

    } 

}


第三步,将Test类上验证手机号的正则表达式换成@Mobile注解

    //手机号码也用个性化提示,使用正则表达式进行匹配,非空时不验证
    //@Pattern(regexp="^1(3|4|5|7|8)\\d{9}$",message="手机号码格式错误!")
    @Mobile
    private String userMobile;

总结,这里面使用的注解和一些方法都是来自org.springframework.validation.annotation的架包,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值