SpringBoot项目优雅的进行参数校验之JSR303,Hibernate validator

项目使用SpringBoot进行开发

maven依赖:

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

 查看依赖可以看到Hibernate validator已被spring boot starter web 集成,所以无需再另外引入依赖即可使用

 一、JSR303、Hibernate validator框架介绍

JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。注解如下:

@AssertFalse:被注释的元素必须为false。

@AssertTrue:被注释的元素必须为true。

@DecimalMax:带注释的元素必须是一个数值,其值必须小于或等于指定的最大值。

@DecimalMin:带注释的元素必须是一个数值,其值必须高于或等于指定的最小值。

@Digits:带注释的元素必须是支持的可接受范围内的数字。

@Email:字符串必须是格式正确的电子邮件地址。

@Future:带注释的元素必须是将来的一个瞬间、日期或时间。

@FutureOrPresent:带注释的元素必须是当前或将来的瞬间、日期或时间。

@Max:带注释的元素必须是一个数值,其值必须小于或等于指定的最大值。

@Min:带注释的元素必须是一个数值,其值必须大于或等于指定的最大值。

@Negative:带注释的元素必须是严格的负数(即0被视为无效值)。

@NegativeOrZero:带注释的元素必须是负数或0。

@NotBlank:带注释的元素不能是 null ,并且必须至少包含一个非空白字符。

@NotEmpty:带注释的元素不能是 null ,也不能为空。

@NotNull:带注释的元素不能是 null 。

@Null:带注释的元素必须是 null 。

@Past:带注释的元素必须是过去的一个瞬间、日期或时间。

@PastOrPresent:带注释的元素必须是过去或现在的瞬间、日期或时间。

@Pattern:带注释的字符串必须与指定的正则表达式匹配。

@Positive:带注释元素必须是严格的正数(即0被视为无效值)。

@PositiveOrZero:带注释的元素必须是正数或0。

@Size:带注释的元素大小必须在指定的边界(包括)之间。

……其余注解可去源码中查看

注解具体支持的类型可去源码中查看。

二、简单参数校验

 1、JavaBean中添加校验注解

package com.example.demo.dto;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

public class PersonInfoDto {
    /**
     * 姓名
     */
    @NotBlank(message = "姓名不能为空!")
    private String name;
    /**
     * 出生日期
     */
    @NotBlank(message = "出生日期不能为空!")
    private String birthday;
    /**
     * 电话
     */
    @Pattern(regexp = "\\d{3}-\\d{8}|\\d{4}-\\{7,8}", message = "电话号码格式不正确!")
    private String phone;
    /**
     * 邮箱
     */
    @Email(message = "邮箱格式不正确!")
    @NotBlank(message = "邮箱不能为空!")
    private String email;

    //get、set省略...

}

2、在controller中使用参数校验

package com.example.demo.controller;

import com.example.demo.dto.PersonInfoDto;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("/person")
public class PersonInfoController {
    private static final Logger logger=LoggerFactory.getLogger(PersonInfoController.class);

    @PostMapping("/add")
    public void addPersonInfo(@RequestBody @Valid PersonInfoDto dto){
        logger.info("{}",dto.toString());
    }
}

当访问这个post接口时,如果参数不符合Model中定义的话,程序中就回抛出400异常,并提示错误信息。

 三、自定义校验注解

1、注解定义

package com.example.demo.constraints;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.Pattern;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

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 static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = {PhoneValidatorImpl.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(Phone.List.class)
public @interface Phone {
    /**
     * 必须的属性
     * 显示 校验信息
     * 利用 {} 获取 属性值,参考了官方的message编写方式
     */
    String message() default "必须是真实电话号码!";

    /**
     * 用于分组
     */
    Class<?>[] groups() default { };

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

    /**
     * 正则校验规则,非必须
     */
    String regexp() default "\\d{3}-\\d{8}|\\d{4}-\\{7,8}";

    /**
     *与{@link #regexp()}结合使用,以指定正则表达式选项
     */
    Pattern.Flag[] flags() default { };

    /**
     * 定义List,为了让Bean的一个属性上可以添加多套规则
     *
     * @see Phone
     */
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        Phone[] value();
    }

}

2、注解实现类

package com.example.demo.constraints;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;

public class PhoneValidatorImpl implements ConstraintValidator<Phone,String> {
    private String phoneFormat;

    /**
     * 获取校验规则
     * @param constraintAnnotation
     */
    @Override
    public void initialize(Phone constraintAnnotation) {
        this.phoneFormat=constraintAnnotation.regexp();

    }

    /**
     * 校验逻辑实现
     * @param value 需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value.isEmpty()){
            return true;
        }
        boolean matches = Pattern.matches(this.phoneFormat, value);
        return matches;
    }
}

3、JavaBean添加注解

package com.example.demo.dto;

import com.example.demo.constraints.Phone;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

public class PersonInfoDto {
    
    //其它参数...

    /**
     * 电话
     */
    @Phone
    private String phone;
  
    //get、set省略...
}

使用方法同上。

四、分组校验

对同一个JavaBean,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证

1、定义两个空接口,分别代表增加和修改是的校验规则

package com.example.demo.validated;

public interface AddValidated {
}
package com.example.demo.validated;

public interface UpdateValidated {
}

2、JavaBean上添加注解时指明分组

package com.example.demo.dto;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;

/**
 * @author: shenzeyu
 * @date: 2019.09.23
 */
public class PersonInfoDto {
    
    //其它参数...

    /**
     * 邮箱
     */
    @Email(message = "邮箱格式不正确!",groups = {AddValidated.class})
    @NotBlank(message = "邮箱不能为空!",groups = {UpdateValidated.class})
    private String email;

    //get、set省略...
}

3、接口处启用校验

package com.example.demo.controller;

import com.example.demo.dto.PersonInfoDto;

import com.example.demo.validated.AddValidated;
import com.example.demo.validated.UpdateValidated;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/person")
public class PersonInfoController {
    private static final Logger logger=LoggerFactory.getLogger(PersonInfoController.class);

    @PostMapping("/add")
    public void addPersonInfo(@RequestBody @Validated(value = AddValidated.class) PersonInfoDto dto){
        logger.info("{}",dto.toString());
    }

    @PostMapping("/update")
    public void updatePersonInfo(@RequestBody @Validated(value = UpdateValidated.class) PersonInfoDto dto){
        logger.info("{}",dto.toString());
    }
}

备注:此处@Validated(AddValidated.class) 表示使用AddValidated这套校验规则,若使用@Valid 则表示使用默认校验规则

@Validated与@Valid的区别可参考此篇文章https://blog.csdn.net/qq_27680317/article/details/79970590

五、Spring validator 方法级别校验

JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,spring 在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验

1、实例化MethodValidationPostProcessor

package com.example.demo.config;

import org.springframework.context.annotation.Bean;

public class MethodValidationPostProcessor {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

2、在所要实现方法参数校验的类上面添加@Validated,及在方法上面添加校验规则:

package com.example.demo.controller;

import com.example.demo.dto.PersonInfoDto;

import com.example.demo.validated.AddValidated;
import com.example.demo.validated.UpdateValidated;
import org.hibernate.validator.constraints.Length;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/person")
@Validated
public class PersonInfoController {
    private static final Logger logger=LoggerFactory.getLogger(PersonInfoController.class);

   //其它接口省略

    @GetMapping("/get")
    public void getPersonInfo(@Length(max = 3) String name){
        logger.info("{}",name);
    }

    @PostMapping("/add")
    public void addPersonInfo(@RequestBody @Validated(value = AddValidated.class) PersonInfoDto dto){
        logger.info("{}",dto.toString());
    }

}

 

六、结语

本文章仅仅只是对JSR303,Hibernate validator做了简单介绍,关于参数检验的方式还有很多,后续可能会继续研究。

文章中如有不正之处,望请斧正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值