@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated

article/2025/10/1 8:56:45

技术栈:

spring boot 2.3.3.RELEASE

hibernate-validator

文末附项目源代码

 

目录

简述

项目依赖

全局异常处理类

基础参数校验

实体类

控制类

测试

嵌套参数验证

实体类

控制类

测试

分组参数验证

接口类

实体类

控制类

测试

 @Valid和@Validated 区别


简述

@Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束。

@Validated作用在类、方法和参数上

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {Class<?>[] value() default {};
}

错误的状态码

返回的响应码推荐使用400 bad request.

所有参数注解含义

项目依赖

Maven 依赖坐标:

说明:在演示项目中所有实体类均在包entity,控制层均在包controller。

全局异常处理类

说明:若不做异常处理,@Validated注解的默认异常消息如下(示例):

2020-09-05 21:48:38.106  WARN 9796 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.validateddemo.controller.DemoController.validatedDemo1(com.example.validateddemo.entity.dto.UseDto): [Field error in object 'useDto' on field 'username': rejected value [null]; codes [NotBlank.useDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [useDto.username,username]; arguments []; default message [username]]; default message [用户名不能为空!]] ]

 因此我们在这里做了一个全局的异常处理类,用于处理参数校验失败后抛出的异常,同时进行日志输出。

package com.example.validateddemo.handler;import com.example.validateddemo.base.Result;
import com.example.validateddemo.enums.ResultEnum;
import com.example.validateddemo.utils.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
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.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;/*** @author He Changjie on 2020/9/5*/
@Slf4j
@ControllerAdvice
public class ValidatedExceptionHandler {/*** 处理@Validated参数校验失败异常* @param exception 异常类* @return 响应*/@ResponseBody@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(MethodArgumentNotValidException.class)public Result exceptionHandler(MethodArgumentNotValidException exception){BindingResult result = exception.getBindingResult();StringBuilder stringBuilder = new StringBuilder();if (result.hasErrors()) {List<ObjectError> errors = result.getAllErrors();if (errors != null) {errors.forEach(p -> {FieldError fieldError = (FieldError) p;log.warn("Bad Request Parameters: dto entity [{}],field [{}],message [{}]",fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());stringBuilder.append(fieldError.getDefaultMessage());});}}return ResultUtil.validatedException(stringBuilder.toString());}
}

基础参数校验

实体类

package com.example.validateddemo.entity.dto;import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;import javax.validation.constraints.*;/*** 用户实体* 数据传输对象* @author He Changjie on 2020/9/5*/
@Data
public class User1Dto {/*** 用户名*/@NotBlank(message = "用户名不能为空!")private String username;/*** 性别*/@NotBlank(message = "性别不能为空!")private String gender;/*** 年龄*/@Min(value = 1, message = "年龄有误!")@Max(value = 120, message = "年龄有误!")private int age;/*** 地址*/@NotBlank(message = "地址不能为空!")private String address;/*** 邮箱*/@Email(message = "邮箱有误!")private String email;/*** 手机号码*/@Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!")private String mobile;
}

控制类

package com.example.validateddemo.controller;import com.example.validateddemo.entity.dto.Use1Dto;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author He Changjie on 2020/9/5*/
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {@PostMapping("/insert")public String validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){System.out.println(use1Dto);return "success";}
}

测试

1、参数校验通过:

2、参数校验不通过:

嵌套参数验证

验证实体中的其他需要被验证的对象集合或其他对象

实体类

package com.example.validateddemo.entity.dto;import lombok.Data;import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;/*** 队伍实体* 数据传输对象* @author He Changjie on 2020/9/5*/
@Data
public class Team1Dto {/*** 队伍名称*/@NotBlank(message = "队伍名称不能为空!")private String name;/*** 队伍人员*/@NotNull(message = "队伍人员不能为空!")@Validprivate List<User1Dto> userList;/*** 队伍负责人*/@NotNull(message = "队伍负责人不能为空!")@Validprivate User1Dto user;
}

控制类

package com.example.validateddemo.controller;import com.example.validateddemo.base.Result;
import com.example.validateddemo.entity.dto.Team1Dto;
import com.example.validateddemo.entity.dto.Use1Dto;
import com.example.validateddemo.utils.ResultUtil;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author He Changjie on 2020/9/5*/
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {@PostMapping("/insert")public Result validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){return ResultUtil.success(use1Dto);}@PostMapping("/insert2")public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto){return ResultUtil.success(team1Dto);}
}

测试

1、参数验证通过:

2、参数验证不通过:

分组参数验证

将不同的校验规则分给不同的组,在使用时,指定不同的校验规则

接口类

package com.example.validateddemo.interfaces;/*** 校验分组1* @author He Changjie on 2020/9/5*/
public interface Group1 {
}

 

package com.example.validateddemo.interfaces;/*** 校验分组2* @author He Changjie on 2020/9/5*/
public interface Group2 {
}

实体类

package com.example.validateddemo.entity.dto;import com.example.validateddemo.interfaces.Group1;
import com.example.validateddemo.interfaces.Group2;
import lombok.Data;import javax.validation.constraints.*;/*** @author He Changjie on 2020/9/5*/
@Data
public class User2Dto {/*** 用户名*/@NotBlank(message = "用户名不能为空!", groups = {Group1.class})private String username;/*** 性别*/@NotBlank(message = "性别不能为空!")private String gender;/*** 年龄*/@Min(value = 1, message = "年龄有误!", groups = {Group1.class})@Max(value = 120, message = "年龄有误!", groups = {Group2.class})private int age;/*** 地址*/@NotBlank(message = "地址不能为空!")private String address;/*** 邮箱*/@Email(message = "邮箱有误!", groups = {Group2.class})private String email;/*** 手机号码*/@Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!", groups = {Group2.class})private String mobile;
}

控制类

package com.example.validateddemo.controller;import com.example.validateddemo.base.Result;
import com.example.validateddemo.entity.dto.Team1Dto;
import com.example.validateddemo.entity.dto.User1Dto;
import com.example.validateddemo.entity.dto.User2Dto;
import com.example.validateddemo.interfaces.Group1;
import com.example.validateddemo.interfaces.Group2;
import com.example.validateddemo.utils.ResultUtil;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author He Changjie on 2020/9/5*/
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {@PostMapping("/insert")public Result validatedDemo1(@Validated @RequestBody User1Dto user1Dto){return ResultUtil.success(user1Dto);}@PostMapping("/insert2")public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto){return ResultUtil.success(team1Dto);}@PostMapping("/insert3")public Result validatedDemo3(@Validated @RequestBody User2Dto user2Dto){return ResultUtil.success(user2Dto);}@PostMapping("/insert4")public Result validatedDemo4(@Validated(Group1.class) @RequestBody User2Dto user2Dto){return ResultUtil.success(user2Dto);}@PostMapping("/insert5")public Result validatedDemo5(@Validated(Group2.class) @RequestBody User2Dto user2Dto){return ResultUtil.success(user2Dto);}
}

测试

1、未分组校验通过:

2、未分组参数校验不通过:


3、分组1参数校验通过

4、分组1参数校验不通过

5、分组2参数校验通过

6、分组2参数校验不通过

7、使用默认分组,参数校验通过:

说明:将控制层/insert3接口调整如下后测试

@PostMapping("/insert3")public Result validatedDemo3(@Validated(Default.class) @RequestBody User2Dto user2Dto){return ResultUtil.success(user2Dto);}

Default.class为Validated依赖中含有的接口类,非自定义接口类

8、使用默认分组,参数校验不通过:

说明:同第7点相同操作

 @Valid和@Validated 区别

通过源码分析:

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {Class<?>[] value() default {};
}

@Valid:没有分组的功能。

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能

 

码云地址:https://gitee.com/jie_harris/validateddemo.git

 

手编不易,转载请注明来源

谢谢!


http://chatgpt.dhexx.cn/article/AGmmuiJo.shtml

相关文章

@Valid与@Validated的区别

1.介绍 说明&#xff1a; 其实Valid 与 Validated都是做数据校验的&#xff0c;只不过注解位置与用法有点不同。 不同点&#xff1a; &#xff08;1&#xff09; Valid是使用Hibernate validation的时候使用。Validated是只用Spring Validator校验机制使用。 &#xff08;2&…

优雅的使用Validated

前言 Validation是一套帮助我们继续对传输的参数进行数据校验的注解&#xff0c;通过配置Validation可以很轻松的完成对数据的约束。 这个技术一直都想进行梳理了很多复杂校验不会使用&#xff0c;本章节用来进行梳理 validated 用于参数的校验&#xff0c;支持简单和复杂(分组…

@Valid与@Validated区别

1.Valid与Validated作用 Valid与Validated都是用来校验接收参数的。Valid是使用Hibernate validation的时候使用Validated是只用Spring Validator校验机制使用说明&#xff1a;java的JSR303声明了Valid这类接口&#xff0c;而Hibernate-validator对其进行了实现。Validated与V…

java中换行符_Java 中的空格符、换行符等怎么表示

展开全部 特殊字符的自表示方法&#xff1a;&#xff1a; 1、\t 空格2113 (\u0009) 2、\n 换行 (\u000A) 3、\\ 反斜杠5261 4、\r 回车 (\u000D) 5、\d 数字4102等价1653于[0-9] 6、\D 非数字等价于[^0-9] 7、\s 空白符号 [\t\n\x0B\f\r] 8、\S 非空白符号 [^\t\n\x0B\f\r] 9、…

java通用文件换行符_java通用文件换行符

java通用文件换行符 [2021-02-07 00:14:46] 简介: java中的换行符是【\n】和【\r】。二者的区别是:【\r】表示回车,【\n】表示新行,但两者都可以实现换行。具体实现方法如:【System.out.print("\n")】。在java中,可以使用\n和\r来 php如何去掉换行符 2020-09-0…

java 处理换行符_Java 文件换行符识别与转换

项目经验,如需转载,请注明作者:Yuloran (t.cn/EGU6c76) 背景 项目开发需要手动合入几十种语言的翻译到 string.xml 中,这是一件非常痛苦的事情:Copy、Paste,Copy、Paste,Copy、Paste... 人都快疯了!被逼无奈写了个自动替换翻译的工具,原理很简单:解析 Excel中的翻译,…

java回车换行符linux,回车换行符 java

回车换行符 java [2021-01-31 11:28:50] 简介: java中的换行符是【\n】和【\r】。二者的区别是:【\r】表示回车,【\n】表示新行,但两者都可以实现换行。具体实现方法如:【System.out.print("\n")】。在java中,可以使用\n和\r来 php如何去掉换行符 2020-09-02 p…

java 分行符_java换行符有哪些

在java中可以使用的换行符有&#xff1a;"\n"表示新行、“\r”表示回车&#xff0c;\r \n”。 在java中需要在字符串(文本)末尾包含换行符"\n"&#xff0c;“\r”或者“\r \n”就可以进行换行。 换行符"\n" "\n"也称换行&#xff0c;一…

java换行符如何使用

java换行符的使用方法&#xff1a;1、换一行【System.out.print(" “)】&#xff1b;2、换两行【System.out.println(” “)】&#xff1b;3、换三行【System.out.println(” " )】。 java换行符的使用方法&#xff1a; 可以使用Java中 和 的换行&#xff0c;不过也…

java:换行符“\n”

换行符 一、关键代码二、简单说明三、源码&#xff08;一&#xff09;、源码A&#xff08;二&#xff09;、源码A的运行效果 四、结语五、定位日期 一、关键代码 java的换行符 "\n" 两次换行&#xff08;相当于摁两次回车键的效果&#xff09; System.out.println(&…

关于ping命令出现大量dup原因

(DUP!) 这个东西是DUPLICATE的一个缩写&#xff0c;也就是ping包的时候收到多个重复值回应&#xff0c;通常发生在linux环境下比较多&#xff0c;windows环境对ping的reply只接到第一个&#xff0c;后续的重复回应会被废弃。 发生这种情况通常是主宿机的IP冲突&#xff0c;或者…

c语言 字符串dup,C语言中dup和dup2函数的不同和使用

在unix高级编程中有介绍dup和dup2&#xff0c;但是没有实例说明&#xff0c; 笔者自己结合实例进行了测试了解。 在linux下&#xff0c;通过open打开以文件后&#xff0c;会返回一个文件描述符&#xff0c;文件描述符会指向一个文件表&#xff0c;文件表中的节点指针会指向节点…

汇编语言之 DUP 套 DUP

题目&#xff1a;下列变量各占多少字节。 A1 DW 23H, 5876H A2 DB 3 DUP(?), 0AH, 0DH, $ A3 DW 4 DUP(3 DUP(1, 2, AB)) 为了更直观地进行观察&#xff0c;我们编写如下程序。 DSEG SEGMENTA1 DW 23H, 5876HA2 DB 3 DUP(?), 0AH, 0DH, $A3 DW 4 DUP(3 DUP(1, 2, AB)) DSE…

函数dup和dup2

这两个函数包含在#include<unistd.h>之中。他们都是用来复制一个现有的文件描述符。 函数原型 函数功能&#xff1a;若成功返回新的文件描述符&#xff0c;否则返回-1&#xff0c;并且dup返回的文件描述符一定是当前可用文件描述符中最小的。 dup2可以用newfd指定新描述…

dup2使用

头文件&#xff1a; 参数&#xff1a; dup2是帮助我们进行重定向的系统调用接口 使用方法&#xff1a;把fd_arrays[oldfd]拷贝到fd_arrays[newfd]&#xff0c;实现重定向 原理&#xff1a; 要会使用dup2&#xff0c;要先了解文件描述符fd 文件描述符 下面这段代码就是不调用…

重定向dup和dup2

dup和dup2 返回值是int的文件描述符&#xff0c;参数是已有的文件描述符 此时打印的文件描述符为4&#xff0c;使用这个文件描述符可以访问打开的文件&#xff0c;可以通过这个往新的文件描述符写&#xff0c;主只需把打开方式改成可读可写即可。 dup2&#xff1a; 注意两个指针…

dup dup2 fcntl函数

dup函数 dup函数&#xff1a;用来复制文件描述符 函数原型&#xff1a; int dup(int oldfd);//oldfd为要复制的文件描述符 成功返回最小且没被占用的文件描述符,失败返回-1 dup函数复制的文件描述符与原文件描述符指向同一个文件 //测试dup函数复制文件描述符 #include <std…

【Linux】dup、dup2函数

目录 dup函数dup2函数 橙色 dup函数 int dup(int oldfd); //复制文件描述符 /*#include <unistd.h>int dup(int oldfd);作用&#xff1a;复制一个新的文件描述符fd3, int fd1 dup(fd),fd指向的是a.txt, fd1也是指向a.txt从空闲的文件描述符表中找一个最小的&#xff…

dup、dup2介绍

dup和dup2也是两个非常有用的调用&#xff0c;它们的作用都是用来复制一个文件的描述符。它们经常用来重定向进程的stdin、stdout和stderr。这两个函数的 原形如下&#xff1a;#include <unistd.h> int dup( int oldfd ); int dup2( int oldfd, int targetfd ) …

Linux应用编程之dup函数和dup2函数

在 Linux 系统中&#xff0c; open 返回得到的文件描述符 fd 可以进行复制&#xff0c;复制成功之后可以得到一个新文件描述符&#xff0c;使用新的文件描述符和旧的文件描述符都可以对文件进行 IO 操作&#xff0c;复制得到的文件描述符和旧的文件描述符拥有相同的权限&…