SpringBoot使用JSR303进行数据验证

x33g5p2x  于2021-12-06 转载在 Spring  
字(5.7k)|赞(0)|评价(0)|浏览(383)

SpringBoot支持JSR303的验证规范。

JSR303验证主要是由一系列的注解所组成,这些注解可在导入了hibernate-validator后直接使用。

No.注解说明
1@Null验证对象是否为null
2@NotNull验证对象是否不为null, 无法查检长度为0的字符串
3@NotBlank检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
4@NotEmpty检查约束元素是否为NULL或者是EMPTY.
5@AssertTrue验证 Boolean 对象是否为 true
5@AssertFalse验证 Boolean 对象是否为 false
7@Size(min=, max=)验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
8@Length(min=, max=)验证字符串长度是否在给定范围内
9@Past验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
10@Future验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
11@Pattern验证 String 对象是否符合正则表达式的规则,regexp:正则表达式
12@Min验证 Number 和 String 对象是否大等于指定的值
13@Max验证 Number 和 String 对象是否小等于指定的值
14@DecimalMax被标注的值必须不大于约束中指定的最大值
15@DecimalMin被标注的值必须不小于约束中指定的最小值
16@Digits验证 Number 和 String 的构成是否合法
17@Digits(integer=,fraction=)验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
7@Range(min=, max=)被指定的元素必须在合适的范围内
18@Range(min=,max=)验证Number是否在给定范围内
19@Valid递归的对关联对象进行校验,
20@CreditCardNumber信用卡验证
21@Email验证是否是邮件地址,如果为null,不进行验证,算通过验证。
22@URL验证是否为url

JSR303验证流程如下所示:

一、使用JSR303校验参数

使用JSR303校验需要在接收的VO类上添加相关的验证注解,随后在接收参数上配置@Valid注解。

JSR303验证由Hibernate提供,所以需要在项目中添加maven依赖:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>

编写参数接收类,并配置校验注解:

@Data
public class MessageParams {
    @Range(min = 0, max = 100)
    private Integer id;
    @NotNull
    private String title;
    @NotNull
    private String content;
}

新建MessageAction接收参数MessageParams:

@RestController
@RequestMapping("/message/*")
public class MessageAction {

    @RequestMapping("/echo")
    public Object echo(@Valid MessageParams params) {
        return params;
    }
}

访问:http://localhost:8080/message/echo发现系统出现了由于验证导致的异常。

二、以Rest形式返回错误信息

对于前后端分离的架构来说,直接返回异常信息显然是不可取的,错误信息应以JSON形式返回。
编写返回数据公共格式类:

public class ResultMessage<T> {
    private Integer code; // 返回编码
    private String message; // 信息
    private T result; // 返回结果
    private boolean success;

    public ResultMessage() {}

    public ResultMessage(Integer code, String message, T result, boolean success) {
        this.code = code;
        this.message = message;
        this.result = result;
        this.success = success;
    }
    // setter,getter略
}

开启全局异常配置:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResultMessage<?> exceptionHandler(HttpServletRequest request, Exception e) {
        Map<String, String> resultMap = new LinkedHashMap<>();
        resultMap.put("uri",request.getRequestURI());
        resultMap.put("exception",e.getClass().getName());
        return new ResultMessage(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), resultMap, false);
    }
}

重新启动项目,再次访问:http://localhost:8080/message/echo,此时的错误信息会以JSON形式返回。

三、校验普通参数

如果Controller层方法接受的参数不是VO类型,而是Integer、String这样的基本数据类型,那么校验规则需要写在接收参数上,并且需要在Controller类上配置@Validated注解。

@RestController
@RequestMapping("/validate/*")
@Validated   // 启用JSR303校验
public class ValidateAction {

    @RequestMapping("/get")
    public String get(@NotNull @Length(min = 5, max = 10) String data) {
        return data;
    }
}

访问:http://localhost:8080/validate/get发现NotNull注解配置生效。

四、设置错误信息

进入@NotNull注解源代码发现,其内部有个字段message配置的是验证出错的默认提示信息(实际上错误信息被配置在资源文件中,此处显示的仅是资源的key值),因此可以通过修改message的内容自定义错误提示信息。

修改MessageParams类,添加校验提示信息:

@Data
public class MessageParams {
    @Range(min = 0, max = 100, message = "id应在0-100之间")
    private Integer id;
    @NotNull(message = "标题不能为空")
    private String title;
    @NotNull(message = "内容不能为空")
    private String content;
}

重新启动项目,访问:http://localhost:8080/message/echo,此时显示的是自定义的提示信息。

此时虽然完成了错误信息的自定义显示,但是直接写在注解中不方便后期的修改,一般的做法是写在配置文件中,在resource路径下新建ValidationMessages.properties文件(必须叫这个文件名)。

message.id.range.error=id应在0-100之间
message.title.notnull.error=标题不能为空
message.content.notnull.error=内容不能为空

将MessageParams中JSR303注解的message字段全部改为表达式的形式,引用ValidationMessages.properties中的key。

@Data
public class MessageParams {
    @Range(min = 0, max = 100, message = "{message.id.range.error}")
    private Integer id;
    @NotNull(message = "{message.title.notnull.error}")
    private String title;
    @NotNull(message = "{message.content.notnull.error}")
    private String content;
}

重新启动项目,访问:http://localhost:8080/message/echo得到同样的结果。

五、自定义验证器

仿写@NotNull注解,实现自定义正则校验注解。

新建RegexValidator注解:

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

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Target({FIELD, PARAMETER}) // 该注解允许使用在成员和参数上
@Retention(RUNTIME) // 运行时生效
@Constraint(validatedBy = RegexConstraintValidate.class) // 绑定正则处理类
public @interface RegexValidator { // 自定义正则注解

    String message() default "正则验证出错"; // 错误信息

    Class<?>[] groups() default {}; // 验证分组

    Class<? extends Payload>[] payload() default {}; // 附加数据源信息

    String pattern(); // 接受验证正则
}

新建RegexValidator注解的处理类并实现ConstraintValidator接口:

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

public class RegexConstraintValidate implements ConstraintValidator<RegexValidator, Object> {

    private String regexExpression;

    @Override
    public void initialize(RegexValidator constraintAnnotation) {
        regexExpression = constraintAnnotation.pattern(); // 通过注解内容获取正则表达式
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if(value==null) { // 数据为null,验证失败
            return false;
        }
        return value.toString().matches(regexExpression); // 返回验证结果
    }
}

修改MessageParams类,追加date字段并配置正则验证。

@Data
public class MessageParams {
    @Range(min = 0, max = 100, message = "{message.id.range.error}")
    private Integer id;
    @NotNull(message = "{message.title.notnull.error}")
    private String title;
    @NotNull(message = "{message.content.notnull.error}")
    private String content;
    @RegexValidator(pattern = "\\d{4}\\-\\d{2}\\-\\d{2}")
    private String date;
}

启动项目,访问:http://localhost:8080/message/echo?id=8&title=特大新闻&content=xxx&date=123456

相关文章