前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

相信项目中做一些htttp接口,避免不了要对参数进行校验,大多数情况下,其实我们只是校验是否为NULL就可以了

1、通过注解实现各种状态的字段

1.1、引入依赖

<!--校验-->
<javax-validation.version>2.0.1.Final</javax-validation.version>
<hibernate-validator.version>6.0.9.Final</hibernate-validator.version>

<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions><!-- 去掉默认配置 -->
        <exclusion>
            <artifactId>validation-api</artifactId>
            <groupId>javax.validation</groupId>
        </exclusion>
        <exclusion>
            <artifactId>hibernate-validator</artifactId>
            <groupId>org.hibernate.validator</groupId>
        </exclusion>
    </exclusions>
</dependency>



<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${hibernate-validator.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>validation-api</artifactId>
            <groupId>javax.validation</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <artifactId>validation-api</artifactId>
    <groupId>javax.validation</groupId>
    <version>${javax-validation.version}</version>
</dependency>


1.2、注解

1、如果变量传入的时候是NULL,则不会校验 类似于 @AssertTrue @Size(min=, max=) 等,除非有了后面3的代码

2、为了我们以后方便, 所有校验属性必须加组,方便阅读,如果不加group的话,会提供一个默认的组,校验的时候选择哪个组就会校验哪个

3、配置NULL的组group后,无需配置类似于@Size等里面的组group,

其中 	Hibernate Validator 附加的 constraint  (也就是说如果下面的内容中,不引入hibernate包就不会起作用)
@Email  验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@Length(min=, max=) 验证字符串的长度
@Range(min=,max=,message=)  被注释的元素必须在合适的范围内 
空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.

Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
 
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) 验证字符串的长度
 
日期检查
@Past           验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  

正则表达式
@Pattern(regexp="[1-9]{1,3}", message="数量X: 必须为正整数,并且0<X<1000")   验证 String 对象是否符合正则表达式的规则
比如:@Pattern(message = "accountProperty此版本只支持对公",regexp = "1")


数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min            验证 Number 和 String 对象是否大等于指定的值  
@Max            验证 Number 和 String 对象是否小等于指定的值  
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
范围  
@Range(min=10000,max=50000,message="range.bean.wage")
 
 
@Valid 对象传递参数的时候用到
public String doAdd(Model model, @Valid AnimalForm form, BindingResult result){}

1.3、ValidatorConfiguration

@Configuration
public class ValidatorConfiguration {


    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        processor.setValidator(validator());
        return processor;
    }

    @Bean
    public static Validator validator() {
        return ValidateUtils.validator;
    }
}

1.3、DTO类

1.3.1、JavaBean:被校验对象

package com.hlj.proj.dto.validate;

import com.hlj.proj.common.group.ValidateGroup;
import com.hlj.proj.validate.anno.GreaterLess;
import com.hlj.proj.validate.anno.NameInclude;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;

import javax.validation.Valid;
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.util.List;

/**
 * @author HealerJean
 * @version 1.0v
 * @Description 注意点
 * 1、 下面出了判断空的注解之外,必须有值才回校验
 * 2、 所有校验属性必须加组,方便阅读
 * @ClassName JavaBean
 * @date 2019/4/17  14:08.
 */
@Data
public class JavaBean extends BaseBean {

    @NotBlank(message = "name 为空 ", groups = ValidateGroup.HealerJean.class)
    @Size(min = 1, max = 5, message = "name  @Size(min = 1,max = 5  字符串长度 最低为1 最大为5", groups = ValidateGroup.HealerJean.class)
    private String name;

    @Size(min = 1, max = 2, message = "list @Size(min = 1,max = 2 集合大小 最低为1 最大为2", groups = ValidateGroup.HealerJean.class)
    private List<String> list;

    @Length(min = 1, max = 5, message = "@Length(min = 1,max = 5 字符串长度 最低为1 最大为5", groups = ValidateGroup.HealerJean.class)
    private String strLength;

    @Min(value = 5, message = "strNum  @Min(value = 5,message =  字符串(数字的字符串大小判断)【数字类型的变量都可以】", groups = ValidateGroup.HealerJean.class)
    private String strNum;

    @Range(min = 1, max = 10, message = "strRange @Range(min = 1,max = 10  最小为1,最大为10  ", groups = ValidateGroup.HealerJean.class)
    private String strRange;

    @DecimalMin(value = "100.1", message = "小数值的判断,最小为 100.1", groups = ValidateGroup.HealerJean.class)
    private String strDecimal;

    @Digits(integer = 2, fraction = 2, message = "strDigts  @Digits(integer = 2,fraction = 2  整数最高2位,小数最高2位", groups = ValidateGroup.HealerJean.class)
    private String strDigts;

    @AssertFalse(message = " @AssertFalse 必须为false ", groups = ValidateGroup.HealerJean.class)
    private Boolean assertFalse;

    /**
     * 内部对象校验
     */
    @Valid
    @NotNull(message = "内部对象不能为空", groups = ValidateGroup.HealerJean.class)
    private InnerBean innerBean;

    /**
     * 自定义注解校验
     */
    @NameInclude(message = "类型必须是type value必须是HealerJean", type = "Mail", groups = {ValidateGroup.HealerJean.class})
    @NotBlank(message = "自定义校验不能为空", groups = {ValidateGroup.HealerJean.class})
    private String myName;

    @NotNull(message = "myDec不能为空", groups = {ValidateGroup.HealerJean.class})
    @GreaterLess(min = "1.1", max = "10.5", message = "必须大于1.1 并且小于 10.5 ", groups = {ValidateGroup.HealerJean.class})
    private BigDecimal myDec;


    // 测试非Null下,其他注解属性无需 加 group
    @NotBlank(message = "testGroup不能为空", groups = {ValidateGroup.HealerJean.class})
    //不加组的情况,只要有值就会校验,所以如果需要必须非空,则我们保证了 NotBlank 就可以自动校验下面的了
    @Length(max = 5, message = "testGroup长度不能超过5")
    // @Length(max = 5, message = "testGroup长度不能超过5", groups = {ValidateGroup.HealerJean.class})
    private String testGroup ;

}

1.3.2、BaseBean:父类

package com.hlj.vialidate.data;

import com.hlj.proj.common.group.ValidateGroup;
import lombok.Data;

import javax.validation.constraints.NotBlank;


/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName BaseBean
 * @date 2019/6/11  16:38.
 * @Description
 */
@Data
public class BaseBean {

    @NotBlank(message = "父类String 不能为空",groups = ValidateGroup.HealerJean.class)
    private String fatherString;

}

1.3.3、InnerBean:内部使用使用类

package com.hlj.vialidate.data;

import com.hlj.proj.common.group.ValidateGroup;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotBlank;


/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName InnerBean
 * @date 2019/6/11  16:39.
 * @Description
 */
@Data
public class InnerBean {

    @NotBlank(message = "innerBean不能为空",groups = ValidateGroup.HealerJean.class)
    @Length(max = 2,message = "innerNname 长度最长为2",groups = ValidateGroup.HealerJean.class)
    private String innerNname ;
}

1.4、ValidateUtils:校验工具类

package com.hlj.utils;

import com.hlj.data.general.AppException;
import org.hibernate.validator.HibernateValidator;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * @Description 校验工具
 */
public class ValidateUtils {

    public static Validator validator;

    static {

        validator = Validation
                .byProvider(HibernateValidator.class)
                .configure()
                //快速返回模式,有一个验证失败立即返回错误信息
                .failFast(true)
                .buildValidatorFactory()
                .getValidator();
    }

    /**
     * 静态方法校验使用的
     *
     * @param object
     * @return
     */
    public static String validate(Object object) {
        if(object == null){
            throw new AppException("参数不完整");
        }
        Set<ConstraintViolation<Object>> validate = validator.validate(object);
        return resultValidate(validate);

    }

    /**
     * 静态方法校验使用,并且带分组的
     *
     * @param object
     * @param group
     * @return
     */
    public static String validate(Object object, Class group) {
        if (group == null) {
            return validate(object);
        } else {
            Set<ConstraintViolation<Object>> validate = validator.validate(object, group);
            return resultValidate(validate);
        }
    }


    private static String resultValidate(Set<ConstraintViolation<Object>> validate) {
        if (!validate.isEmpty()) {
            final StringBuffer stringBuffer = new StringBuffer();
            validate.stream().forEach(
                    item -> stringBuffer.append(item.getMessage()).append(","));
            stringBuffer.setLength(stringBuffer.length() - 1);
            return stringBuffer.toString();
        }
        return "success";
    }

}

1.5、ValidateGroup:组

package com.hlj.vialidate;

/**
 * @author HealerJean
 * @version 1.0v
 * @Description
 * @ClassName CoreValidateGroup
 * @date 2019/4/17  9:30.
 */
public class ValidateGroup {

    public interface HealerJean {};

}

1.6 、Controller

package com.hlj.moudle.validate;


import com.hlj.data.general.ResponseBean;
import com.hlj.utils.ValidateUtils;
import com.hlj.vialidate.data.JavaBean;
import com.hlj.vialidate.ValidateGroup;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * @Description
 * @Author HealerJean
 * @Date 2018/3/22  上午10:22.
 */
@ApiResponses(value = {
        @ApiResponse(code = 200, message = "访问正常"),
        @ApiResponse(code = 301, message = "逻辑错误"),
        @ApiResponse(code = 500, message = "系统错误"),
        @ApiResponse(code = 401, message = "未认证"),
        @ApiResponse(code = 403, message = "禁止访问"),
        @ApiResponse(code = 404, message = "url错误")
})
@Api(description = "demo控制器")
@Controller
@RequestMapping("hlj")
@Slf4j
public class VialidateController {


    @ApiOperation(value = "Post接口",
            notes = "Post接口",
            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE,
            response = ResponseBean.class
    )
    @PostMapping( value = "validate",produces="application/json;charset=utf-8")
    @ResponseBody
    public ResponseBean post(@RequestBody JavaBean JavaBean){
        String validate = ValidateUtils.validate(JavaBean,ValidateGroup.HealerJean.class);
        if(!"success".equals(validate)){
            log.info("错误信息:{}", validate);
        }
        return ResponseBean.buildSuccess(validate);
    }


}



####

2、自定注解实现

可以用于字典校验

2.1、自定义注解


package com.hlj.proj.validate.anno;


import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * 自定义注解
 */
@Constraint(validatedBy = {}) //这里可以写NameIncludeValidator.class 但是因为是我接口与实现类分离的,所以这部分业务统一写到了方法里面,如果不是分离的项目,这里直接写上class就可以了
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NameInclude {

    String message(); //报错返回的信息

    Class<?>[] groups() default {}; //被哪个组校验

    String type(); //自己定义的

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

2.2、自定义注解验证数据

package com.hlj.vialidate.inter;


import org.apache.commons.lang3.StringUtils;

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

/**
 * 校验数据
 */
public class NameIncludeValidator implements ConstraintValidator<NameInclude, String> {

    private String type;

    /**
     * 获取注解中的值
     * @param constraintAnnotation
     */
    @Override
    public void initialize(NameInclude constraintAnnotation) {
         type = constraintAnnotation.type();
    }

    /**
     * @param value 字段数据
     * @param context
     * @return
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        if(StringUtils.isBlank(value)){
            return true;
        }
        if("Mail".equals(type)&&"HealerJean".equals(value)){
            return true ;
        }else {
            return false ;
        }
    }
}

2.3、对象中自定义注解的使用

     /**
     * 自定义注解校验
     */
    @NameInclude(message = "类型必须是type value必须是HealerJean" ,type = "Mail",groups = {ValidateGroup.HealerJean.class})
    @NotNull(message = "自定义校验不能为空",groups = {ValidateGroup.HealerJean.class})
    private String myName ;

2.4、测试

POST http://localhost:8888/hlj/validate
Content-Type: application/json; charset=UTF-8

{
    "name": "1234",
    "strLength": "12345",
    "list": [
        "list",
        "list2"
    ],
    "strNum": "6",
    "strRange": "9",
    "strDecimal": "100.2",
    "strDigts": "15.66",
    "fatherString": "fatherString",
    "innerBean": {
        "innerNname": "in"
    },
    "myName": "HealerJean",
    "myDec": 1.2
}

3、包分离项目引入自定义注解

3.1、修改ValidateUtils

注意:因为我的项目是接口与实现分离(api无法导入service包),所以这里写上了自定义校验注解的实现,而不是在自定义注解中使用Constraint进行引入,如果不是接口与实现分离,建议使用@Constraint

//快速返回模式,有一个验证失败立即返回错误信息
static {

    //一般情况下使用这里就可以了
    // validator = Validation
    //         .byProvider(HibernateValidator.class)
    //         .configure()
    //         .failFast(true)
    //         .buildValidatorFactory()
    //         .getValidator();

    //因为我的项目是接口与实现分离(api无法导入service包),所以这里写上了自定义校验注解的实现,而不是在自定义注解中使用Constraint进行引入,如果不是接口与实现分离,建议使用Constraint
    HibernateValidatorConfiguration configure = Validation
        .byProvider(HibernateValidator.class)
        .configure();
    ConstraintMapping greaterLessConstraintMapping = configure.createConstraintMapping();
    ConstraintMapping nameIncludeConstraintMapping = configure.createConstraintMapping();
    nameIncludeConstraintMapping.constraintDefinition(NameInclude.class)
        .validatedBy(NameIncludeValidator.class);
    greaterLessConstraintMapping.constraintDefinition(GreaterLess.class)
        .validatedBy(GreaterLessValidator.class);
    validator = configure
        .addMapping(nameIncludeConstraintMapping)
        .addMapping(greaterLessConstraintMapping)
        .failFast(true)
        .buildValidatorFactory()
        .getValidator();
}
package com.hlj.proj.utils.validate;

import com.hlj.proj.constant.CommonConstants;
import com.hlj.proj.enums.ResponseEnum;
import com.hlj.proj.exception.BusinessException;
import com.hlj.proj.utils.validate.validator.GreaterLessValidator;
import com.hlj.proj.utils.validate.validator.NameIncludeValidator;
import com.hlj.proj.validate.anno.GreaterLess;
import com.hlj.proj.validate.anno.NameInclude;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * @author HealerJean
 * @version 1.0v
 * @ClassName ValidateUtils
 * @Date 2019/8/9  15:37.
 * @Description
 */
public class ValidateUtils {

    public static Validator validator;

    //快速返回模式,有一个验证失败立即返回错误信息
    static {

        //一般情况下使用这里就可以了
        // validator = Validation
        //         .byProvider(HibernateValidator.class)
        //         .configure()
        //         .failFast(true)
        //         .buildValidatorFactory()
        //         .getValidator();

        //因为我的项目是接口与实现分离(api无法导入service包),所以这里写上了自定义校验注解的实现,而不是在自定义注解中使用Constraint进行引入,如果不是接口与实现分离,建议使用Constraint
        HibernateValidatorConfiguration configure = Validation
                .byProvider(HibernateValidator.class)
                .configure();
        ConstraintMapping greaterLessConstraintMapping = configure.createConstraintMapping();
        ConstraintMapping nameIncludeConstraintMapping = configure.createConstraintMapping();
        nameIncludeConstraintMapping.constraintDefinition(NameInclude.class)
                .validatedBy(NameIncludeValidator.class);
        //  如果同一个注解有多个实现过程
        // .validatedBy(DigitsIncloudZeroValidatorForCharSequence.class);
        greaterLessConstraintMapping.constraintDefinition(GreaterLess.class)
                .validatedBy(GreaterLessValidator.class);
        validator = configure
                .addMapping(nameIncludeConstraintMapping)
                .addMapping(greaterLessConstraintMapping)
                .failFast(true)
                .buildValidatorFactory()
                .getValidator();
    }


    /**
     * 静态方法校验使用的
     *
     * @param object
     * @return
     */
    public static String validate(Object object) {
        if (object == null) {
            throw new BusinessException(ResponseEnum.参数错误);
        }
        Set<ConstraintViolation<Object>> validate = validator.validate(object);
        return resultValidate(validate);

    }

    /**
     * 静态方法校验使用,并且带分组的
     *
     * @param object
     * @param group
     * @return
     */
    public static String validate(Object object, Class group) {
        if (object == null) {
            return "校验对象为空";
        }
        if (group == null) {
            return validate(object);
        } else {
            Set<ConstraintViolation<Object>> validate = validator.validate(object);
            if (validate != null && !validate.isEmpty()) {
                return resultValidate(validate);
            }

            Set<ConstraintViolation<Object>> validateGroup = validator.validate(object, group);
            return resultValidate(validateGroup);
        }
    }


    private static String resultValidate(Set<ConstraintViolation<Object>> validate) {
        if (!validate.isEmpty()) {
            final StringBuffer stringBuffer = new StringBuffer();
            validate.stream().forEach(
                    item -> stringBuffer.append(item.getMessage()).append(","));
            stringBuffer.setLength(stringBuffer.length() - 1);
            return stringBuffer.toString();
        }
        return CommonConstants.COMMON_SUCCESS;
    }
}

4、注解工具

4.1、校验数字整数位和小数位,小数位为0时可接受

4.1.1、@interface :DigitsIncloudZero

@Constraint(validatedBy = {})
@Documented
@Target( {  ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DigitsIncloudZero {

    String message() default "{javax.validation.constraints.Digits.message}";

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

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

    int integer();

    int fraction();
}

4.1.2、DigitsIncloudZeroValidatorForNumber

public class DigitsIncloudZeroValidatorForNumber implements ConstraintValidator<DigitsIncloudZero, Number> {


    private int maxIntegerLength;
    private int maxFractionLength;

    @Override
    public void initialize(DigitsIncloudZero constraintAnnotation) {
        this.maxIntegerLength = constraintAnnotation.integer();
        this.maxFractionLength = constraintAnnotation.fraction();
    }

    @Override
    public boolean isValid(Number num, ConstraintValidatorContext context) {
        if ( num == null ) {
            return true;
        }
        boolean integerFlag = false;
        boolean fractionFlag = false;
        if ( maxIntegerLength < 0 ) {
            integerFlag = true;
        }
        if ( maxFractionLength < 0 ) {
            fractionFlag = true;
        }
        if(integerFlag && fractionFlag){
            return true;
        }
        BigDecimal bigNum;
        if ( num instanceof BigDecimal ) {
            bigNum = ( BigDecimal ) num;
        }
        else {
            bigNum = new BigDecimal( num.toString() ).stripTrailingZeros();
        }
        if(!integerFlag) {
            int integerPartLength = bigNum.precision() - bigNum.scale();
            integerFlag = maxIntegerLength >= integerPartLength;
        }
        if(!fractionFlag) {
            int fractionPartLength = bigNum.scale() < 0 ? 0 : bigNum.scale();
            fractionFlag =  maxFractionLength >= fractionPartLength || 0 == bigNum.setScale(0,BigDecimal.ROUND_DOWN).compareTo(bigNum);
        }
        return integerFlag & fractionFlag;
    }

}

4.1.3、DigitsIncloudZeroValidatorForCharSequence

public class DigitsIncloudZeroValidatorForCharSequence implements ConstraintValidator<DigitsIncloudZero, CharSequence> {

    private int maxIntegerLength;
    private int maxFractionLength;

    @Override
    public void initialize(DigitsIncloudZero constraintAnnotation) {
        this.maxIntegerLength = constraintAnnotation.integer();
        this.maxFractionLength = constraintAnnotation.fraction();
    }

    @Override
    public boolean isValid(CharSequence charSequence, ConstraintValidatorContext context) {
        if ( charSequence == null ) {
            return true;
        }
        boolean integerFlag = false;
        boolean fractionFlag = false;
        if ( maxIntegerLength < 0 ) {
            integerFlag = true;
        }
        if ( maxFractionLength < 0 ) {
            fractionFlag = true;
        }
        if(integerFlag && fractionFlag){
            return true;
        }
        BigDecimal bigNum = getBigDecimalValue( charSequence );
        if(!integerFlag) {
            int integerPartLength = bigNum.precision() - bigNum.scale();
            integerFlag = maxIntegerLength >= integerPartLength;
        }
        if(!fractionFlag) {
            int fractionPartLength = bigNum.scale() < 0 ? 0 : bigNum.scale();
            fractionFlag =  maxFractionLength >= fractionPartLength || 0 == bigNum.setScale(0,BigDecimal.ROUND_DOWN).compareTo(bigNum);
        }
        return integerFlag & fractionFlag;
    }

    private BigDecimal getBigDecimalValue(CharSequence charSequence) {
        BigDecimal bd;
        try {
            bd = new BigDecimal( charSequence.toString() );
        }
        catch ( NumberFormatException nfe ) {
            return null;
        }
        return bd;
    }
}

ContactAuthor

 

 
 

代码下载