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

这篇具有很好参考价值的文章主要介绍了还在用 if else 做参数校验?快来学习高级参数校验吧。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

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

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

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

二、自定义校验

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

2.1 定义 GenderArrayValuable 接口

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

package com.ratel.validation.core.validator;

/**
 * 可生成 Int 数组的接口
 */
public interface GenderArrayValuable {

    /**
     * @return int 数组
     */
    int[] array();

}

2.2 定义性别 GenderEnum 枚举类

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

import com.ratel.validation.core.validator.GenderArrayValuable;

import java.util.Arrays;

public enum GenderEnum implements GenderArrayValuable {

    MALE(1, "男"),
    FEMALE(0, "女");

    /**
     * 值数组
     */
    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(GenderEnum::getValue).toArray();

    /**
     * 性别值
     */
    private final Integer value;
    /**
     * 性别名
     */
    private final String name;

    GenderEnum(Integer value, String name) {
        this.value = value;
        this.name = name;
    }

    public Integer getValue() {
        return value;
    }

    public String getName() {
        return name;
    }

    @Override
    public int[] array() {
        return ARRAYS;
    }

}

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

package com.ratel.validation.core.validator;

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

import static java.lang.annotation.ElementType.*;

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = GenderValidator.class)
public @interface GenderCheck {

    /**
     * @return 实现 IntArrayValuable 接口的
     */
    Class<? extends GenderArrayValuable> value();

    /**
     * @return 提示内容
     */
    String message() default "必须在指定范围 {value}";

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

    /**
     * @return Payload 数组
     */
    Class<? extends Payload>[] payload() default {};

    /**
     *  Defines several {@code @GenderCheck} constraints on the same element.
     */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {

       GenderCheck[] value();

    }

}

2.4 自定义约束的校验器 GenderValidator

package com.ratel.validation.core.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

public class GenderValidator implements ConstraintValidator<GenderCheck, Integer> {

    /**
     * 值数组
     */
    private Set<Integer> values;

    @Override
    public void initialize(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());
        }
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        // 1 校验通过
        if (values.contains(value)) {
            return true;
        }
        // 2 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
        context.disableDefaultConstraintViolation(); // 3 禁用默认的 message 的值
        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
                .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 4. 重新添加错误提示语句
        return false; // 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 枚举范围内。


package com.ratel.validation.entity;



import com.ratel.validation.core.validator.GenderCheck;
import com.ratel.validation.enums.GenderEnum;

import javax.validation.constraints.NotNull;

/**
 * 用户更新性别 DTO
 */
public class UserUpdateGenderDTO {

    /**
     * 用户编号
     */
    @NotNull(message = "用户编号不能为空")
    private Integer id;

    /**
     * 性别
     */
    @NotNull(message = "性别不能为空")
    @GenderCheck(value = GenderEnum.class, message = "性别必须是 {value}")
    private Integer gender;

    public Integer getId() {
        return id;
    }

    public UserUpdateGenderDTO setId(Integer id) {
        this.id = id;
        return this;
    }

    public Integer getGender() {
        return gender;
    }

    public UserUpdateGenderDTO setGender(Integer gender) {
        this.gender = gender;
        return this;
    }

    @Override
    public String toString() {
        return "UserUpdateGenderDTO{" +
                "id=" + id +
                ", gender=" + gender +
                '}';
    }

}

2.6 定义一个对外访问接口

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;

@RestController
@RequestMapping("/users")
@Validated
@Api(tags = "用户")
public class UserController {

    private Logger logger = LoggerFactory.getLogger(getClass());
        @PostMapping("/update_gender")
    public String updateGender(@Valid UserUpdateGenderDTO updateGenderDTO) {
        logger.info("[updateGender][updateGenderDTO: {}]", updateGenderDTO);
        return "性别更新成功";
    }
} 

2.7 请求接口 进行验证

我们这里使用 knife4j 接口文档,当我们 把 gender的值传成 非 【0 ,1 】 以外的值,就会直接返回错误信息,"请求参数不合法:性别必须是 [0, 1]",
还在用 if else 做参数校验?快来学习高级参数校验吧

三、总结

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

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

例如说,参数的大小长度,判断参数是否为空,是否是身份证号,是否是邮箱,是否是手机号 这些不依赖 外部数据源的等等,是适合通过 Spring Validation 中完成。
例如说,校验用户名,邮箱,手机号 唯一等等,依赖 外部数据源 的,是不适合通过 Spring Validation中完成。文章来源地址https://www.toymoban.com/news/detail-413368.html

到了这里,关于还在用 if else 做参数校验?快来学习高级参数校验吧的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue学习之v-if/v-else/v-else-if

    v-else/v-else-if 创建 demo7.html,内容如下 效果展示

    2024年02月08日
    浏览(30)
  • JS if else语句详解

    在正常情况下,JavaScript 脚本是按顺序从上到下执行的,这种结构被称为顺序结构。如果使用 if、else/if 或 switch 语句,可以改变这种流程顺序,让代码根据条件选择执行的方向,这种结构被称为分支结构。 if 语句允许根据特定的条件执行特定的语句。语法格式如下: if(expr

    2024年02月15日
    浏览(34)
  • Python if-else 速记

    编程中经常使用速记符号来简化我们的工作。 速记符号是一种可以更简洁、更省时省力地完成工作的方法。 本文将讨论 Python 中使用的速记符号作为 if-else 语句的快捷方式。 如前所述,速记符号是一种可以简洁地编写程序的方法。 到目前为止,我们在 Python 中使用了许多速

    2024年02月11日
    浏览(33)
  • python条件判断语句(if else)

            python中判断语句的基本结构由if和else组成,当if后面的条件为真时,执行if下面的语句;当if后面的条件为假时,执行else下面的语句。这里条件的真假都是用bool值来作为依据的,我们知道比较运算、成员运算、身份运算返回的结果都是bool值。所以比较运算、成员运算

    2024年02月09日
    浏览(46)
  • HarmonyOS-if-else-条件渲染

    ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,使用if、else和else if渲染对应状态下的UI内容。 说明 从API version 9开始,该接口支持在ArkTS卡片中使用。 支持if、else和else if语句。 if、else if后跟随的条件语句可以使用状态变量。 允许在容器组件内使用,通过条件

    2024年01月23日
    浏览(41)
  • 策略模式解决if-else问题

    释义: 策略模式是一种行为设计模式,它允许在运行时根据不同的情况来选择不同的策略。 这种模式支持开闭原则,在不修改现有代码的前提下,动态的添加、删除、替换算法。 组成部分: 策略接口(Strategy) :它是一个接口,具体的策略实现类去实现这个接口,就可以提供

    2024年02月03日
    浏览(28)
  • C 语言教程:条件和 if...else 语句

    您已经学习过 C 语言支持数学中的常见逻辑条件: 小于: a b 小于或等于: a = b 大于: a b 大于或等于: a = b 等于: a == b 不等于: a != b 您可以使用这些条件来根据不同的决策执行不同的操作。 C 语言具有以下条件语句: 使用 if 来指定要执行的代码块,如果指定的条件为真

    2024年02月04日
    浏览(46)
  • #if、 #ifdef、#else、#endif等宏详解

    这些都是 条件编译 命令 #ifdef 语句,对应 #endif 语句,可以区隔一些与特定 头文件 、 程序库 和其他 文件版本 有关的代码。可翻译为:如果宏定义了语句1则执行程序2。 #ifdef 等宏是为了进行条件编译。一般情况下, 源程序中所有的行都参加编译 。但是有时希望对其中一部

    2023年04月13日
    浏览(28)
  • 「PHP系列」If...Else语句/switch语句

    PHP 中的 if...else 语句是用于根据条件执行不同代码块的强大工具。这种结构允许你基于某个条件(通常是布尔表达式)的结果来决定执行哪一部分代码。下面是对 if...else 语句的详细解释以及一些示例。 示例 1:基本 if…else 结构 在这个例子中,如果 $number 大于 5,将输出 “

    2024年04月27日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包