JSR 303参数校验 Bean Validation

参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。

那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的。

在使用之前我们可以在Spring Boot工程引入该jar包。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

简单示例

同样,我先举一个简单的例子来说明如何使用JSR 303来进行参数校验。假设一个情景,我们需要使用手机号码和密码,来登陆一个网站。

之前我们在LoginController中,可能需要做很多繁琐的验证,是否为空之类的,现在我们使用JSR 303参数校验,就能够优雅的解决这个问题。

LoginController.java 登陆部分映射

    @RequestMapping("/do_login")
    @ResponseBody
    //必须用@Valid修饰LoginVo
    public Result<Boolean> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {
        log.info(loginVo.toString());
        //登录
        userService.login(response, loginVo);
        return Result.success(true);
    }

LoginVo.java

import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;

public class LoginVo {

    @NotNull              //不能为空
    @IsMobile             //@IsMobile是自定义校验规则,必须符合电话号码规范。见IsMobile类和IsMobileValidator类
    private String mobile;

    @NotNull              //密码不能为空
    @Length(max=32)       //长度不能超过32
    private String password;

    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "LoginVo [mobile=" + mobile + ", password=" + password + "]";
    }
}

一个 constraint 通常由 annotation 和相应的 constraint validator 组成,它们是一对多的关系。

@IsMobile是一个定制化的 constraint,由两个内置的 constraint 组合而成。看我们自己编写的IsMobile类和IsMobileValidator类。我们可以自己定义参数校验的规则
IsMobile.java

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.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class })   //调用IsMobileValidator来进行校验
public @interface  IsMobile {

    boolean required() default true;

    String message() default "手机号码格式错误";           //如果校验不通过提示的信息

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

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

IsMobileValidator.java

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; 
import org.apache.commons.lang3.StringUtils;
import com.seckill.util.ValidatorUtil;

//实现ConstraintValidator的两个方法
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

    private boolean required = false;

    //初始化方法
    public void initialize(IsMobile constraintAnnotation) {
        required = constraintAnnotation.required();
    }

    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(required) {
            return ValidatorUtil.isMobile(value);    //
        }else {
            if(StringUtils.isEmpty(value)) {        //如果为空,参数校验通过
                return true;                   
            }else {                                 //如果不为空,检验手机号格式是否通过
                return ValidatorUtil.isMobile(value);  
            }
        }
    }
}

简单的通过正则表达式来判断电话号码开头是否为1,长度是否合格。
ValidatorUtil.java

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;

public class ValidatorUtil {

    private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");

    public static boolean isMobile(String src) {
        if(StringUtils.isEmpty(src)) {
            return false;
        }
        Matcher m = mobile_pattern.matcher(src);
        return m.matches();
    }
}

JSR 303 - Bean Validation中有很多内置的constraint
这里写图片描述

使用案例

public class Order { 
     // 必须不为 null, 大小是 10 
     @NotNull 
     @Size(min = 10, max = 10) 
     private String orderId; 
     // 必须不为空
     @NotEmpty 
     private String customer; 
     // 必须是一个电子信箱地址
     @Email 
     private String email; 
     // 必须不为空
     @NotEmpty 
     private String address; 
     // 必须不为 null, 必须是下面四个字符串'created', 'paid', 'shipped', 'closed'其中之一
     @NotNull 
     private String status; 
     // 必须不为 null 
     @NotNull 
     private Date createDate; 
     // 嵌套验证
     @Valid 
     private Product product; 

    …
     getter 和 setter 
 }

这样看来,我们将参数校验整合处理,是否比每次使用时再一个个去判断参数是否符合条件所写的代码更加优雅呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值