03 后端入参校验:自定义注解实现

news2024/12/24 2:32:40

03 后端入参校验:自定义注解实现

  • 一、前言
  • 二、实现
    • 1、新建Spring Boot项目
    • 2、引入依赖
    • 3、新建注解类
    • 4、新建校验器
    • 5、全局异常处理器
    • 6、编写Controller
    • 7、新建实体类
    • 8、启动并测试

在这里插入图片描述

一、前言

Java 后端开发中,为了实现入参校验,常常会使用一些特定的注解来标记参数的约束条件。这些注解通常与一个校验框架(如 Spring Boot 提供的 @Valid 等)配合使用,以便在方法调用前自动进行参数校验。以下是一些常见的用于入参校验的注解:

  • @NotNull: 适用于任何对象,包括基本类型、包装类型、集合类、Map等,判断对象本身是否为null,但是无法校验空字符串。

  • @NotEmpty: 适用于集合类(如 ListSet)、数组、MapCharSequence(如 String )等,判断是否非空(即长度不为0)。

  • @NotBlank: 专门用于 CharSequence(如 String )。不仅可以校验null,同时还可以校验空字符串

如果以上注解实际情况都不能用,需要自定义注解取代它们。

二、实现

1、新建Spring Boot项目

此步骤略过~

2、引入依赖

可以根据 GAV坐标 ,去 Maven官网 查询依赖版本。
Maven Repository: Search/Browse/Explore

<!-- Hibernate Validator: 强大的数据验证框架 -->
<dependency>  
    <groupId>org.hibernate.validator</groupId>  
    <artifactId>hibernate-validator</artifactId>  
    <version>版本号</version>  
</dependency>

<!-- Spring Messaging: 处理消息传递相关的注解。如:@Payload -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>当前Spring框架版本号</version>
</dependency>

3、新建注解类

package com.demo.springboot3.ParamValid;

import jakarta.validation.Constraint;
import org.springframework.messaging.handler.annotation.Payload;

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

/**
 * @Description: // 参数校验:注解
 * @Author: M.
 * @Date: 2024-04-25 18:35
 */
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ParamValidator.class)
public @interface ParamValid {
    // message支持自定义
    String message() default  "该字段不能为空!";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
  • @Target
    定义: 指定一个注解能够合法应用的位置。
属性简述
ANNOTATION_TYPE可应用于其他注解类型的定义
CONSTRUCTOR可应用于构造函数声明
FIELD可应用于字段(变量)声明,包括实例变量、静态变量和枚举常量
LOCAL_VARIABLE可应用于方法或块内部的局部变量声明
METHOD可应用于方法声明,包括实例方法、静态方法和抽象方法
PACKAGE可应用于源文件或目录结构开头的包声明
PARAMETER可应用于方法、构造函数或lambda表达式中的参数声明
METHOD可应用于类、接口(包括注解类型)或枚举声明
  • @Retention:
    定义: 指定一个自定义注解在何时何地保持其存在性。
属性简述详细描述
SOURCE源码级别保留这种策略下,注解只存在于源代码中,编译器不会把它们写入到编译后的字节码文件(.class 文件)中。因此,这些注解对于编译器在编译时有用(例如,触发代码生成或编译时检查),但在编译后的任何阶段(如类加载、运行时)都不可见。适用于编译时辅助工具和 IDE 插件。
CLASS类文件级别保留这是默认的保留策略,如果未明确指定 @Retention,则采用此策略。在这种情况下,注解会保留在编译后的字节码文件中,但对运行时环境不可见。这对于编译器、类加载器等工具在加载类时进行处理非常有用,如代码分析工具、构建工具或某些依赖于字节码分析的框架。
RUNTIME运行时级别保留当注解指定为运行时保留时,它们不仅存在于源代码中,而且会被编译器写入字节码文件,并且在程序运行时仍可通过反射 API 被访问到。这意味着应用程序或其他运行时库可以通过查询类或对象的注解来做出运行时决策,实现动态行为或元数据驱动的功能。
  • @Constraint:
    定义:Bean Validation框架中@Constraint 是一个元注解,用于标记一个类为自定义验证约束注解。
属性简述
validatedBy指定实现验证逻辑的校验器类
message定义当约束验证失败时显示的错误消息
groups用于分组验证,允许在不同上下文中执行不同的验证集
payload携带额外信息,供验证引擎或校验器使用,通常不直接使用

4、新建校验器

package com.demo.springboot3.ParamValid;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import java.util.Collection;
import java.util.Map;

/**
 * @Description: // 参数校验:校验器
 * @Author: M.
 * @Date: 2024-04-25 19:17
 */
public class ParamValidator implements ConstraintValidator<ParamValid, Object> {
    @Override
    public void initialize(ParamValid constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
        return value != null // 参数不为null
                && !((value instanceof CharSequence && ((CharSequence) value).length() == 0) // 字符串不为空,同时校验了空字符串
                || (value instanceof Collection && ((Collection<?>) value).isEmpty()) // 集合不为空
                || (value instanceof Map && ((Map<?,?>) value).isEmpty())); // Map不为空
    }
}
  • ParamValid: 上文定义的注解名称
  • Object: 校验器的入参,需要检验的参数。

5、全局异常处理器

package com.demo.springboot3.ParamValid;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: // 参数校验:全局异常处理器
 * @Author: M.
 * @Date: 2024-04-25 19:35
 */
@ControllerAdvice
public class PVGlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<Map<String, Object>> handleValidationExceptions(MethodArgumentNotValidException ex, HttpServletRequest request){
        Map<String, Object> body = new HashMap<>();
        body.put("timestamp", new Date());
        body.put("status", HttpStatus.BAD_REQUEST.value());
        body.put("path",request.getRequestURI());

        List<Map<String, String>> errorMessages = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fileError -> {
                    Map<String, String> error = new HashMap<>();
                    error.put("field", fileError.getField());
                    error.put("message", fileError.getDefaultMessage());
                    return error;
                })
                .collect(Collectors.toList());

        body.put("errors", errorMessages);
        return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
    }
}
  • @ControllerAdvice
    定义:Spring 框架中用于全局处理控制器(@Controller 或其派生注解如 @RestController)中异常和数据绑定问题的一个特殊注解。
    使用场景: 全局异常处理、数据绑定验证、模型属性添加。

  • @ExceptionHandler
    定义: @ExceptionHandler 注解的方法会在目标控制器方法抛出指定异常类型时被调用,用于捕获和处理异常,生成合适的响应结果返回给客户端。

  • @ResponseStatus
    定义:@ExceptionHandler 方法上,当该方法处理异常并返回时,应返回的 HTTP 状态码。

📣拓展:
如果各位同学使用了 jdk9 及以上版本,可以使用一下jdk新特性 Map.of() 方法。

List<Map<String, String>> errorMessages = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fileError -> {
                    Map<String, String> error = new HashMap<>();
                    error.put("field", fileError.getField());
                    error.put("message", fileError.getDefaultMessage());
                    return error;
                })
                .collect(Collectors.toList());

优化后代码如下:

List<Map<String, String>> errorMessages = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fileError -> Map.of(
                        "field", fileError.getField(),
                        "message", fileError.getDefaultMessage()
                ))
                .collect(Collectors.toList());

6、编写Controller

package com.demo.springboot3.controller;

import com.demo.springboot3.entity.User;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description: // 用户信息:Controller
 * @Author: M.
 * @Date: 2024-04-25 20:11
 */
@RestController
@Validated
public class UserController {
    @PostMapping("/query")
    public String queryUser(@Valid @RequestBody User user) {
        System.out.println(user);
        return "success";
    }
}
  • @Validated
    定义: Spring 提供的一个批注,用于启用 JSR-303/JSR-349Bean Validation 规范)数据校验功能。它通常应用于控制器方法的参数、控制器类或服务类上,指示 Spring 在方法调用前对指定的 Java 对象进行数据验证。@Validated@Valid 注解配合使用,后者用于标记需要验证的参数或字段。

7、新建实体类

package com.demo.springboot3.entity;

import com.demo.springboot3.ParamValid.ParamValid;
import lombok.Data;

import java.util.List;

/**
 * @Description: // 用户类
 * @Author: M.
 * @Date: 2024-04-25 20:00
 */
@Data
public class User implements java.io.Serializable{
    @ParamValid(message = "用户名(name)不能为空!!!")
    private String name;
    @ParamValid(message = "用户名(age)不能为空!!!")
    private Integer age;
    @ParamValid(message = "用户名(addressList)不能为空!!!")
    private List<String> addressList;
}
  • @ParamValid: 此注解就是上文新建的参数校验注解。需要放在实体类属性上方。

8、启动并测试

在这里插入图片描述
在这里插入图片描述


本文隶属于个人专栏:00 个人小笔记📋📋📋
到这里 03 后端入参校验:自定义注解实现 就结束了!!!🎉🎉🎉
欢迎小伙伴们学习和指正!!!😊😊😊
祝大家学习和工作一切顺利!!!😎😎😎

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

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

相关文章

外径合格与否对线缆品质有着直接影响 应用测径仪很重要

关键词&#xff1a;测径仪,线缆测径仪,电缆测径仪,外径测量仪,线缆,电缆 电缆尺寸不合格的危害 1、传输性能下降&#xff1a;尺寸不合格会导致电线电缆的参数不符合设计要求&#xff0c;从而影响传输的速率和稳定性&#xff0c;从而导致数据传输缓慢&#xff0c;影响用户的使用…

PC 自动化测试入门 - pywinauto 上篇:初识

文章目录 前言PC 自动化测试 是什么&#xff1f;常用 PC 自动化测试工具pywinauto 是什么&#xff1f;Windows上支持的可访问性技术列表 操作记事本自动写入问题app Application(backend"uia").start("notepad.exe") 无法正常启动组件选择器和 print_cont…

使用这 7 个绩效评估模板简化您的员工评估

绩效评估受到了不好的评价&#xff1b;员工发现它们压力很大&#xff0c;而管理者则发现它们很耗时。 但随着绩效管理成为 2024 年人力资源的首要任务&#xff0c;也许是时候重新思考了。绩效评估模板可以帮助减轻评估过程的麻烦。通过为管理者提供一种简单、标准化的方法来评…

CVE-2024-3116 PgAdmin8.4代码执行漏洞

前言 在有闲情的时候&#xff0c;看了一下最近的CVE&#xff0c;看到了pgAdmin4在8.4版本之前存在着一个远程代码执行漏洞&#xff0c;因为pgAdmin4在github是开源的&#xff0c;网上也没有看到分析文章&#xff0c;于是就把源码下载了下来&#xff0c;根据漏洞的描述大致的分…

Linux服务器终端软件termius以及Xshell + WinSCP组合

1. termius 官网地址&#xff1a;https://termius.com/ Termius是一个跨平台的SSH客户端&#xff0c;它提供了一个便捷的方式来远程连接和管理服务器、虚拟机和网络设备。以下是Termius的一些特点和功能&#xff1a; 跨平台支持&#xff1a;Termius可在多个操作系统上运行&…

JS事件循环、宏任务与微任务

在JavaScript中&#xff0c;事件循环&#xff08;Event Loop&#xff09;是处理异步操作的核心机制。它负责执行代码&#xff0c;处理事件&#xff0c;并在适当的时候调度回调。为了更好地理解JavaScript的执行模型&#xff0c;我们需要深入探讨事件循环、宏任务&#xff08;Ma…

分布式与一致性协议之Raft算法(二)

Raft算法 什么是任期 我们知道&#xff0c;议会选举中的领导者是有任期的&#xff0c;当领导者任命到期后&#xff0c;需要重新再次选举。Raft算法中的领导者也是有任期&#xff0c;每个任期由单调递增的数字(任期编号)标识。比如&#xff0c;节点A的任期编号是1。任期编号会…

基于Docker + Locust的数据持久化性能测试系统

前几天给大家分享了如何使用Locust进行性能测试&#xff0c;但是在实际使用中会发现存在压测的结果无法保存的问题&#xff0c;比如在分布式部署情况下进行压测&#xff0c;每轮压测完成需要释放资源删除容器重新部署后&#xff0c;这段时间的压测结果就都丢失了&#xff0c;如…

Nacos 安全零信任实践

作者&#xff1a;柳遵飞 Nacos 作为配置中心经常存储一些敏感信息&#xff0c;但是由于误用导致安全风险&#xff0c;最常见的主要是以下两个问题&#xff1a; 1&#xff09;Nacos 暴露公网可以吗&#xff1f;不可以&#xff0c;因为 Nacos 定位是注册配置中心&#xff0c;是…

【STM32+HAL】SDIO模式读写SD卡

一、准备工作 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 二、所用工具 1、芯片&#xff1a; STM32F407ZGT6 2、IDE&#xff1a; MDK-Keil软件 3、库文件&#xff1a;STM32F4xxHAL库 三、实现功能 实现用DMA读写SD卡内…

Hadoop3:集群搭建及常用命令与shell脚本整理(入门篇,从零开始搭建)

一、集群环境说明 1、用VMware安装3台Centos7.9虚拟机 2、虚拟机配置&#xff1a;2C&#xff0c;2G内存&#xff0c;50G存储 3、集群架构 从表格中&#xff0c;可以看出&#xff0c;Hadoop集群&#xff0c;主要有2部分&#xff0c;一个是HDFS服务&#xff0c;一个是YARN服务 …

CSS中的层叠上下文

HTML 文档中的三维概念 平时我们从设备终端看到的 HTML 文档都是一个平面的&#xff0c;事实上 HTML 文档中的元素却是存在于三个维度中。除了大家熟悉的平面画布中的 x 轴和 y 轴&#xff0c;还有控制第三维度的 z 轴。 其中 x 轴通常用来表示水平位置&#xff0c;y 轴来表示…

力扣刷题Day2

题目链接&#xff1a; 24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 效果&#xff1a; 解题思路&#xff1a; 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 注意不可以只是单纯的改变节点内部的值&#xff0c;而…

面试:MYSQL(SQL优化、MYSQL事务)

目录 一、SQL优化 1、如何定位慢查询 &#xff08;1&#xff09;方案一&#xff1a;开源工具 &#xff08;2&#xff09;方案二&#xff1a;慢日志查询 2、定位到慢查询时&#xff0c;如何优化 3、什么是索引 &#xff08;1&#xff09;底层结构 4、聚簇索引&#xff0…

linux学习:线程安全(信号量+互斥锁读写锁+条件变量+可重入函数)

目录 信号量 有名信号量 步骤 api 创建、打开一个POSIX有名信号量 对 POSIX 有名信号量进行 P、V 操作 关闭、删除 POSIX 有名信号量 例子 无名信号量 步骤 api 初始化、销毁 POSIX 无名信号量 互斥锁读写锁 例子 两条线程 使用互斥锁来互斥地访问标准输出 在加锁…

UE Snap03 启动参数设置

UE Snap03 启动参数设置 UE打包后传入自定义参数及解析。 void UGameInstance::StartGameInstance() {Super::StartGameInstance();UE_LOG(LogTemp, Warning, TEXT("--StartGameInstance--"));FString param;FParse::Value(FCommandLine::Get(), TEXT("-UserN…

# 谷歌 Chrome 浏览器无法安装插件的解决方法

谷歌 Chrome 浏览器无法安装插件的解决方法 运用开发模式安装 安装步骤&#xff1a; 1、 将 XX.crx 插件的扩展名改成 .zip 或者 .rar 并解压到文件夹 XX 目录。 1&#xff09;如&#xff1a;下载的 前端框架 vue.js 插件 nhdogjmejiglipccpnnnanhbledajbpd-6.6.1-Crx4Chro…

Isaac Sim 2 (学习笔记4.26)

今天一整天都要开会&#xff0c;闲的无聊&#xff0c;把这周学的东西简单整理下。纯英文文档想不起来东西的时候总是找不到位置...持续更新一整天 1.将块与块连接起来 尝试连接块与块的时候发现只能是cube、mesh连接&#xff0c;如果是一整个的包括坐标系、材质包等等&#xf…

阿里云服务器购买和设置

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;服务器❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 1、搜索阿里云网址&#xff1a; 2、点击产品&#xff0c;选择云服务器ECS 3、选择立即购买 4、选…

C# Web控件与数据感应之 Control 类

目录 关于数据感应 Control 类 范例运行环境 simpleDataListEx方法 设计 实现 调用示例 数据源 调用 小结 关于数据感应 数据感应也即数据捆绑&#xff0c;是一种动态的&#xff0c;Web控件与数据源之间的交互&#xff0c;诸如 ListControl 类类型控件&#xff0c;在…