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

news2024/10/6 22:22:21

文章目录

  • 一、前言
  • 二、自定义校验
    • 2.1 定义 GenderArrayValuable 接口
    • 2.2 定义性别 GenderEnum 枚举类
    • 2.3 自定义 @GenderCheck 自定义约束注解
    • 2.4 自定义约束的校验器 GenderValidator
    • 2.5 定义 UserUpdateGenderDTO
    • 2.6 定义一个对外访问接口
    • 2.7 请求接口 进行验证
  • 三、总结

一、前言

在上一篇文章 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]",
在这里插入图片描述

三、总结

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

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

例如说,参数的大小长度,判断参数是否为空,是否是身份证号,是否是邮箱,是否是手机号 这些不依赖 外部数据源的等等,是适合通过 Spring Validation 中完成。
例如说,校验用户名,邮箱,手机号 唯一等等,依赖 外部数据源 的,是不适合通过 Spring Validation中完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从C出发 17 --- 函数调用

从表面上来看&#xff0c;函数就是一个代码片段&#xff0c;只不过说这个代码片段可以反复利用&#xff0c;通过调用的方式反复利用&#xff0c;通过函数调用&#xff0c;我们可以将参数传到函数所对应的代码片段里面&#xff0c;然后代码片段去处理这些参数&#xff0c;得到一…

Linux下Samba服务器的安装与配置(简单实用)

为了可以实现Linux与windows之间实现文件的共享&#xff0c;方便文件可以直接修改&#xff0c;而不是像以前需要拷贝文件再进行修改&#xff0c;samba的诞生是为了实现现在的这些需求。我们知道Linux之间可以使用NFS服务器来实现文件的共享&#xff0c;samba的诞生就是为了使wi…

Spring Cloud Security

Spring Cloud Security Spring Cloud Security用于构建微服务的安全应用程序和服务&#xff0c;它可以轻松实现微服务系统架构下的统一安全认证与授权。 Spring Cloud Security 有以下组件。 spring-cloud-security&#xff1a;为Zuul、Feign、Oauth 2.0 的Resource Serve…

ChatGPT5是否会影响人类的发展和工作?

目录前言ChatGPT5是什么ChatGPT5 的潜在影响挑战与风险总结前言 ChatGPT的普及也带来了大量的讨论&#xff0c;关于它是否会影响人类的发展和工作。本文将讨论 ChatGPT5 如何可能改变人类的工作和发展&#xff0c;以及潜在的利弊和挑战。在话题开始之前&#xff0c;让我们先从…

QxOrm的使用-数据操作--增删改查

文章目录QxOrm的使用-数据操作使用QxOrm对数据库进行增删改查新增数据删除数据修改数据查询数据QxOrm的使用-数据操作 上一篇我们讲了QxOrm的基本的数据映射操作&#xff0c;这里面再补充一点东西 数据类型映射 Qt/C类型数据库类型boolSMALLINTqx_boolSMALLINTshortSMALLINT…

【谷粒商城之整合阿里云OSS对象存储】

本笔记内容为尚硅谷谷粒商城整合阿里云OSS对象存储部分 目录 一 、简介 二、云存储开通与使用 1、开通阿里云对象存储服务 2、创建bucket 3、创建子用户&#xff08;获取密钥访问OSS服务器&#xff09; 给该子账户添加权限 4、阿里云对象存储上传方式 三、整合 1、…

BUUCTF--Web篇详细wp

BUUCTF--Web篇详细wp[极客大挑战 2019]EasySQL[极客大挑战 2019]Havefun[HCTF 2018]WarmUp[ACTF2020 新生赛]Include[ACTF2020 新生赛]Exec[强网杯 2019]随便注[GXYCTF2019]Ping Ping Ping[SUCTF 2019]EasySQL[极客大挑战 2019]Secret File[极客大挑战 2019]LoveSQL[极客大挑战…

MySQL 分布式数据库实现:无需修改代码,轻松实现分布式能力

这个项目做什么 ShardingSphere-Proxy&#xff0c;可以让用户像使用原生数据库一样使用 Apache ShardingSphere。 了解一项技术的开始&#xff0c;一般从官网开始。先来看一看官网对 ShardingSphere-Proxy 的定义是什么样的&#xff1a; 定位为透明化的数据库代理端&#xff…

异配图神经网络——Graph Transformer Networks

一.论文概述 作者提出了Graph Transformer Network (GTN)用来在异配图&#xff08;heterogeneous graph&#xff09;上学习节点表示。通过Graph Transformer层&#xff0c;模型能将异构图转换为由meta-path定义的多个新图&#xff0c;这些meta-paths具有任意的边类型和长度&am…

运行Spring Boot项目时[ java: 错误: 不支持发行版本 17 ]

项目场景&#xff1a; 使用IDEA的Spring Initializr构建的Spring boot项目在构建完成后运行出错 问题描述&#xff1a; 用Spring Initializr创建了Spring Boot 项目后&#xff0c;运行时报错&#xff1a; “错误:java: 错误: 不支持发行版本 17”根据错误信息得知&#xff…

Vue学习笔记(5. 计算属性,监视器(侦听器))

1. 计算属性&#xff08;computed&#xff09; (1) get方式 初期显示 改变值后&#xff08;hello -> hello1&#xff09;计算属性allStr跟随变更 (2) get set方式 页面初期显示 改变值&#xff08;hello -> hello1&#xff09;计算属性的get会监控到变更&#xff0c;使…

国产SSD、内存卷哭国外大厂,三星宣布减产涨价在路上了

PC 圈有一句话是这么说的&#xff1a;论价格屠夫还得看国产品牌&#xff01; 可不是嘛&#xff0c;国产长鑫、长江算是彻底将全球存储芯片市场搅局者这一「骂名」坐实了&#xff01; 不说特别早期&#xff0c;前几年吧&#xff0c;普通单条 8G DDR4 内存都能卖到六七百元&…

C++ 红黑树

1.红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;因…

D. Many Perfect Squares

题目链接&#xff1a;Problem - D - Codeforces 题意&#xff1a;给你一个数组&#xff0c;大小不超过50个。 问你让他们全部加上一个x&#xff0c;构造出来最多能够有多少个完全平方数。 思路&#xff1a; 先对数组排个序&#xff0c;首先它最少一定是有一个的&#xff0c…

Git配置SSH步骤

一、git 配置 &#xff08;1&#xff09;打开 git 命令窗口 &#xff08;2&#xff09;配置用户名&#xff08;填自己的姓名&#xff09; git config --global user.name “linjiaxiaozhu” &#xff08;3&#xff09;配置用户邮箱&#xff08;填自己的邮箱&#xff09; git…

电脑录屏的视频保存在哪里?您可以这样查看

案例&#xff1a;电脑录屏之后保存到哪里去了&#xff1f; “前几天&#xff0c;根据网络上的录屏教程试着录制了一下我的电脑屏幕&#xff0c;录制完成之后却找不到录制的视频。有没有小伙伴知道电脑录屏的视频保存在哪里&#xff1f;怎样才能快速找到&#xff1f;” 在现代…

pandas数据聚合和重组

介绍pandas数据聚合和重组的相关知识&#xff0c;仅供参考。 目录 1GroupBy技术 1.1简介 1.2对分组进行迭代 1.3选取一个或一组列 1.4通过字典或Series进行分组 1.5利用函数进行分组 2数据聚合 2.1简介 2.1面向列的多函数应用 2.2以‘无索引’的方式返回聚合数据 1G…

Faster R-CNN

目录 1. Fast R-CNN的不足 2. Faster R-CNN 3. RPN(Region Proposal Network) 3.1 anchor 3.2 RPN 网络 3.3 RPN 网络的损失 4. Faster R-CNN 损失 5. Faster R-CNN 训练 6. 对比 1. Fast R-CNN的不足 Fast R-CNN 的算法流程 Fast R-CNN网络运行速度慢的最主要原因&a…

浅谈根号分治与分块

文章目录1. 根号分治哈希冲突2. 线性分块引入教主的魔法[CQOI2011] 动态逆序对[国家集训队] 排队[HNOI2010] 弹飞绵羊蒲公英1. 根号分治 哈希冲突 题目1 nnn 个数&#xff0c;mmm 次操作。操作 1 为修改某一个数的值&#xff0c;操作 2 为查询所有满足下标模 xxx 等于 yyy 的…

一、基础算法6:双指针算法 模板题+算法模板(最长连续不重复子序列,数组元素的目标和,判断子序列)

文章目录算法模板双指针算法模板最长连续不重复子序列模板暴力法双指针算法数组元素的目标和模板判断子序列模板模板题最长连续不重复子序列原题链接题目题解数组元素的目标和原题链接题目题解判断子序列原题链接题目题解算法模板 双指针算法模板 for (int i 0, j 0; i <…