使用Hibernate Validator进行REST接口上的数据校验

本文部分内容来自互联网。

在进行REST接口开发的时候,需要对POST/PUT等接口传入的参数进行一些校验的时候,可以通过Hibernate Validator提供的注解进行校验。这些注解放在参数实体定义的属性或者类上面,能够进行默认的校验。
配合@Valid注解和BindingResult功能一起使用,则可以在Controller的具体实现逻辑中进行校验后的处理。

1. 常用的校验注解

注解 功能
@NotNull 值不为空
@Null 值只能为空
@Pattern(regex=) 字符串必须匹配括号内的正则表达式
@Size(max=,min=) 集合的元素数量必须max和min之间的值
@CreditCardNumber(ignoreNonDigitCharacters=) 字符串必须是美国标准的信用卡卡号
@Email 字符串必须是email地址
@Length(min=,max=) 检查字符串的长度
@NotBlank 字符串必须有字符
@NotEmpty 字符串不为null,集合有元素
@Range(min=,max=) 数字必须大于min,小于max
@SafeHtml 字符串是安全的html代码
@URL 字符串是合法的url
@AssertFalse 值必须是false
@AssertTrue 值必须是true
@DecimalMax(value=,inclusive=) 值必须小于等于(inclusive=true)/值必须小于(inclusive=false):可以注解在字符串类型上
@DecimalMin(value=,inclusive=) 值必须大于等于(inclusive=true)/值必须大于(inclusive=false):可以注解在字符串类型上
@Max(value=) 值必须小于等于value指定的值,不能注解在字符串类型上
@Min(value=) 值必须大于等于value执行的值,不能注解在字符串类型上
@Digits(integer=,fraction=) 数字格式检查,integer指定整数部分的长度,fraction指定小数部分的长度
@Futrue 值必须是未来的注解
@Past 值只能是过去的时间

2. 在服务中使用校验

例如,通过请求添加一个用户信息,

  1. 那么首先需要在这个用户的定义时确定好要满足哪些信息。比如,用户名不能为空,密码不能为空等。
public class UserInfo {
    private int id;
    @NotBlank
    private String name;
    @NotNull
    private String passwd;
    @Range(min=10, message = "年龄不能小于10")
    private int age;
    private String mail;
    private String phoneNumber;
    private String address;
}
  1. 通过REST的post接口添加用户,则用户信息的body体必须满足这些条件,否则的话controller的服务中,就要校验,并捕获这些异常。
/**
     * 添加一个新的用户
     */
    @PostMapping(value = "/user")
    public UserInfo addOneSpecifiedUser(@Valid UserInfo userInfo, BindingResult errors) {
        if(errors.hasErrors()){
            errors.getAllErrors().stream().forEach(
                    error -> logger.error(error.toString())
            );
        }

        logger.info("进入到用户添加的页面");
        return userInfo;
    }

@Valid注解的功能是检查REST请求推送来的请求体是否满足预定义的格式,如果不满足,那么这些请求中的错误信息将会被BindingResult绑定的变量errors所接收,并带着这些异常进入请求响应代码,由请求响应部分去处理这些异常。

  1. 使用REST工具发送这样的请求后,产生的效果如下:
2018-04-25 10:41:09.303 ERROR 3240 --- [nio-8080-exec-9] o.l.s.controller.UserController          : Field error in object 'userInfo' on field 'age': rejected value [1]; codes [Range.userInfo.age,Range.age,Range.int,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.age,age]; arguments []; default message [age],9223372036854775807,10]; default message [年龄不能小于10]
2018-04-25 10:41:09.303 ERROR 3240 --- [nio-8080-exec-9] o.l.s.controller.UserController          : Field error in object 'userInfo' on field 'passwd': rejected value [null]; codes [NotNull.userInfo.passwd,NotNull.passwd,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userInfo.passwd,passwd]; arguments []; default message [passwd]]; default message [不能为null]

3. 自定义校验

要实现类似于本文中提供那些常用校验方式同样的便利,需要自定义我们自己的校验规则注解。

自定义注解进行校验的步骤

  1. 写一个校验注解,在注解中指定校验器类,校验注解与校验器一般一一对应。
  2. 写一个校验器类并在校验器类中写校验逻辑,校验器必须实现ConstraintValidator接口,第一个参数是对应的注解,第二个参数是要校验的属性的类型。

代码实例

S1: 自定义校验注解

package com.kunlun.validation.annotation;

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

import javax.validation.Constraint;
import javax.validation.Payload;

import com.kunlun.validation.validator.KlPatternValidator;

/**
 * 自定义的校验注解
 * 规则:
 *  1.如果字符串为空串或者为null,则不进行正则校验
 *  2.如果字符串不为空串,则必须进行正则校验
 * @author xc
 * @date 2018年1月19日上午11:38:02
 */
@Documented
// 指定该注解可以使用的地方
@Target(value= {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
// 指定实际进行校验的校验器,该校验器是自己写的且必须实现ConstraintValidator接口
@Constraint(validatedBy=KlPatternValidator.class)
public @interface KlPattern {
    /*
     * 用于验证的注解下列这三个方法必须要,这是Hibernate Validation框架要求的,否则程序再在调用的时候会报错
     * default用于对属性给定默认值
     *  如果不给定默认值,则在使用注解的时候必须给属性指定属性值,否则报错
     *  给定默认值时,在使用注解的时候可以不用指定属性值
     */
    String message() default "不符合正则!";

    Class[] groups() default {};

    Class[] payload() default {};

    // 没加default给定默认值,使用注解的时候该属性必须赋值,否则报错
    String regex();
    // value属性,加上了default "mercy" 使得该属性在使用注解的时候可以不用输入也不会报错
    String value() default "mercy";
}

S2: 与上面校验注解对应的校验器类

package com.kunlun.validation.validator;

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

import com.kunlun.validation.annotation.KlPattern;

/**
 * KlPatternValidator是KlPattern注解实际调用的验证器
 * 在KlPatternValidator中完成校验逻辑
 * 
 * @author xc
 * @date 2018年1月19日上午11:44:38
 */
public class KlPatternValidator implements ConstraintValidator {

    private String regex;

    /**
     * 通过initialize()可以获取注解里的属性值
     */
    @Override
    public void initialize(KlPattern constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
        regex = constraintAnnotation.regex();
    }

    /**
     * 实际验证逻辑
     *  返回值为true表示验证通过,
     *  返回值为false表示验证未通过
     */
    @Override
    public boolean isValid(String s, ConstraintValidatorContext ctx) {

        // 当前前端传过来的请求参数是空串,或者没传的时候,不进行后续正则校验
        if ("".equals(s) || s == null) {
            return true;
        }

        // 进行正则校验
        if(s.matches(regex)) {
            return true;
        }

        return false;
    }

}

你可能感兴趣的