0


还在用 if else 做参数校验?快来学习高级参数校验吧

文章目录

一、前言

在上一篇文章 Springboot实现优雅的参数校验(Spring Validation)和 if else说再见,我们介绍了 Spring Validation 的初级用法,在实际开发中,无论是 Bean Validation 定义的约束,还是 Hibernate Validator 附加的约束,都是无法满足我们复杂的业务场景。所以,我们需要自定义约束。开发自定义约束一共只要两步:

  1. 编写自定义约束的注解;
  2. 编写自定义的校验器 ConstraintValidator 。

下面,就让我们一起来实现一个自定义约束,用于校验参数必须在枚举值的范围内吧。

二、自定义校验

比如一个很常见的用户注册的场景,一般都要限定用户性别,这里我们不考虑 同性 未知的情况哈,只考虑 男和女 这种情况。0:代表女性,1:代表男性,

2.1 定义 GenderArrayValuable 接口

为了方便我们后面获取枚举类型值的,我先定义一个接口 GenderArrayValuable,这里的定义了一个 返回一个int[] 数据的方法。后面我们会在 GenderEnum 中实现这个接口。

packagecom.ratel.validation.core.validator;/**
 * 可生成 Int 数组的接口
 */publicinterfaceGenderArrayValuable{/**
     * @return int 数组
     */int[]array();}

2.2 定义性别 GenderEnum 枚举类

GenderEnum 枚举类实现了 GenderArrayValuable 的 IntArrayValuable 接口,返回值数组 ARRAYS。

importcom.ratel.validation.core.validator.GenderArrayValuable;importjava.util.Arrays;publicenumGenderEnumimplementsGenderArrayValuable{MALE(1,"男"),FEMALE(0,"女");/**
     * 值数组
     */publicstaticfinalint[] ARRAYS =Arrays.stream(values()).mapToInt(GenderEnum::getValue).toArray();/**
     * 性别值
     */privatefinalInteger value;/**
     * 性别名
     */privatefinalString name;GenderEnum(Integer value,String name){this.value = value;this.name = name;}publicIntegergetValue(){return value;}publicStringgetName(){return name;}@Overridepublicint[]array(){return ARRAYS;}}

2.3 自定义 @GenderCheck 自定义约束注解

packagecom.ratel.validation.core.validator;importjavax.validation.Constraint;importjavax.validation.Payload;importjava.lang.annotation.Documented;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importstaticjava.lang.annotation.ElementType.*;@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})@Retention(RetentionPolicy.RUNTIME)@Documented@Constraint(validatedBy =GenderValidator.class)public@interfaceGenderCheck{/**
     * @return 实现 IntArrayValuable 接口的
     */Class<?extendsGenderArrayValuable>value();/**
     * @return 提示内容
     */Stringmessage()default"必须在指定范围 {value}";/**
     * @return 分组
     */Class<?>[]groups()default{};/**
     * @return Payload 数组
     */Class<?extendsPayload>[]payload()default{};/**
     *  Defines several {@code @GenderCheck} constraints on the same element.
     */@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})@Retention(RetentionPolicy.RUNTIME)@Documented@interfaceList{GenderCheck[]value();}}

2.4 自定义约束的校验器 GenderValidator

packagecom.ratel.validation.core.validator;importjavax.validation.ConstraintValidator;importjavax.validation.ConstraintValidatorContext;importjava.util.Arrays;importjava.util.Collections;importjava.util.Set;importjava.util.stream.Collectors;publicclassGenderValidatorimplementsConstraintValidator<GenderCheck,Integer>{/**
     * 值数组
     */privateSet<Integer> values;@Overridepublicvoidinitialize(GenderCheck annotation){GenderArrayValuable[] values = annotation.value().getEnumConstants();if(values.length ==0){this.values =Collections.emptySet();}else{this.values =Arrays.stream(values[0].array()).boxed().collect(Collectors.toSet());}}@OverridepublicbooleanisValid(Integer value,ConstraintValidatorContext context){// 1 校验通过if(values.contains(value)){returntrue;}// 2 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
        context.disableDefaultConstraintViolation();// 3 禁用默认的 message 的值
        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate().replaceAll("\\{value}", values.toString())).addConstraintViolation();// 4. 重新添加错误提示语句returnfalse;// 5 }}

实现 ConstraintValidator 接口。

  • 第一个泛型为 A extends Annotation ,设置对应的自定义约束的注解。例如说,这里我们设置了 @GenderEnum 注解。
  • 第二个泛型为 T ,设置对应的参数值的类型。例如说,这里我们设置了 Integer 类型。

实现

initialize(A constraintAnnotation)

方法,获得 @GenderEnum 注解的 values() 属性,获得值数组,设置到 values 属性种。

实现

boolean isValid(T var1, ConstraintValidatorContext var2); 

方法,实现校验参数值,是否在 values 范围内。
在注释 1 处,校验参数值在范围内,直接返回 true ,校验通过。
在注释 2 处,校验不通过,自定义提示语句。
在注释 5 处,校验不通过,所以返回 false 。
至此,我们已经完成了自定义约束的实现。

2.5 定义 UserUpdateGenderDTO

定一个 UserUpdateGenderDTO 实体类,在 gender 字段上添加自定义的

 @GenderCheck(value = GenderEnum.class, message = "性别必须是 {value}")

注解,限制传入的参数值,必须在 GenderEnum 枚举范围内。

packagecom.ratel.validation.entity;importcom.ratel.validation.core.validator.GenderCheck;importcom.ratel.validation.enums.GenderEnum;importjavax.validation.constraints.NotNull;/**
 * 用户更新性别 DTO
 */publicclassUserUpdateGenderDTO{/**
     * 用户编号
     */@NotNull(message ="用户编号不能为空")privateInteger id;/**
     * 性别
     */@NotNull(message ="性别不能为空")@GenderCheck(value =GenderEnum.class, message ="性别必须是 {value}")privateInteger gender;publicIntegergetId(){return id;}publicUserUpdateGenderDTOsetId(Integer id){this.id = id;returnthis;}publicIntegergetGender(){return gender;}publicUserUpdateGenderDTOsetGender(Integer gender){this.gender = gender;returnthis;}@OverridepublicStringtoString(){return"UserUpdateGenderDTO{"+"id="+ id +", gender="+ gender +'}';}}

2.6 定义一个对外访问接口

importio.swagger.annotations.Api;importio.swagger.annotations.ApiOperation;importio.swagger.annotations.Tag;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.bind.annotation.*;importjavax.validation.Valid;importjavax.validation.constraints.Min;@RestController@RequestMapping("/users")@Validated@Api(tags ="用户")publicclassUserController{privateLogger logger =LoggerFactory.getLogger(getClass());@PostMapping("/update_gender")publicStringupdateGender(@ValidUserUpdateGenderDTO updateGenderDTO){
        logger.info("[updateGender][updateGenderDTO: {}]", updateGenderDTO);return"性别更新成功";}}

2.7 请求接口 进行验证

我们这里使用 knife4j 接口文档,当我们 把 gender的值传成 非 【0 ,1 】 以外的值,就会直接返回错误信息,

"请求参数不合法:性别必须是 [0, 1]",

在这里插入图片描述

三、总结

希望阅读完本文,能够让各位 c友 更加舒适且优雅的完成各种需要参数校验的地方。 不说了,赶紧给自己的系统去把参数校验给补全,嘿嘿。

当然,有一点要注意,Bean Validation 更多做的是,无状态的参数校验。怎么理解呢?

例如说,参数的大小长度,判断参数是否为空,是否是身份证号,是否是邮箱,是否是手机号 这些不依赖 外部数据源的等等,是适合通过

Spring Validation

中完成。
例如说,校验用户名,邮箱,手机号 唯一等等,依赖 外部数据源 的,是不适合通过

Spring Validation

中完成。

标签: 学习 java spring

本文转载自: https://blog.csdn.net/weter_drop/article/details/130131581
版权归原作者 T-OPEN 所有, 如有侵权,请联系我们删除。

“还在用 if else 做参数校验?快来学习高级参数校验吧”的评论:

还没有评论