@Valid和@Validated 区别
作者:互联网
@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.
所有参数注解含义
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email`
项目依赖
Maven 依赖坐标:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhiqiang</groupId>
<artifactId>validated_demo</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<!-- starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<!-- hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
</dependencies>
</project>
说明:在演示项目中所有实体类均在包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.zhiqiang.validated.demo.ExceptionHandler;
import com.zhiqiang.validated.demo.base.Result;
import com.zhiqiang.validated.demo.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;
@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.zhiqiang.validated.demo.entity.dto;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.*;
/**
* 用户实体
* 数据传输对象
*/
@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.zhiqiang.validated.demo.controller;
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;
import com.zhiqiang.validated.demo.entity.dto.User1Dto;
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
/* 测试json
{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
**/
// http://localhost:8071/api/v1/insert0
@PostMapping("/insert0")
public String validatedDemo0(@Validated @RequestBody User1Dto use1Dto){
System.out.println(use1Dto);
return "success";
}
// http://localhost:8071/api/v1/insert
@PostMapping("/insert")
public Result validatedDemo1(@Validated @RequestBody User1Dto use1Dto){
return ResultUtil.success(use1Dto);
}
}
测试
{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
1、参数校验通过:
响应
{
"code": 0,
"msg": "操作成功",
"data": {
"username": "zhangsan",
"gender": "man",
"age": 18,
"address": "shanghai",
"email": "100000@qq.com",
"mobile": "13756707007"
}
}
2、参数校验不通过:
请求
{
"username":"zhangsan",
"gender":null,
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应
{
"code": 6,
"msg": "性别不能为空!",
"data": null
}
2021-09-23 09:24:26.072 WARN 4480 --- [nio-8071-exec-9] c.z.v.d.E.ValidatedExceptionHandler : Bad Request Parameters: dto entity [user1Dto],field [gender],message [性别不能为空!]
嵌套参数验证
验证实体中的其他需要被验证的对象集合或其他对象
实体类
package com.zhiqiang.validated.demo.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 = "队伍人员不能为空!")
@Valid
private List<User1Dto> userList;
/**
* 队伍负责人
*/
@NotNull(message = "队伍负责人不能为空!")
@Valid
private User1Dto user;
}
控制类
package com.zhiqiang.validated.demo.controller;
import com.zhiqiang.validated.demo.base.Result;
import com.zhiqiang.validated.demo.entity.dto.Team1Dto;
import com.zhiqiang.validated.demo.entity.dto.User2Dto;
import com.zhiqiang.validated.demo.interfaces.Group1;
import com.zhiqiang.validated.demo.interfaces.Group2;
import com.zhiqiang.validated.demo.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;
import com.zhiqiang.validated.demo.entity.dto.User1Dto;
import javax.validation.groups.Default;
/**
* @Author LZQ
* @Date 2021/9/22
**/
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
/* 测试json
{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
**/
// http://localhost:8071/api/v1/insert0
@PostMapping("/insert0")
public String validatedDemo0(@Validated @RequestBody User1Dto use1Dto){
System.out.println(use1Dto);
return "success";
}
// http://localhost:8071/api/v1/insert
@PostMapping("/insert")
public Result validatedDemo1(@Validated @RequestBody User1Dto use1Dto){
return ResultUtil.success(use1Dto);
}
// http://localhost:8071/api/v1/insert2
@PostMapping("/insert2")
public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto) {
return ResultUtil.success(team1Dto);
}
}
测试
{
"name":"刘志强说的都队",
"userList":[
{
"username":"王昭君",
"gender":"male",
"age":18,
"address":"shanghai",
"email":"100001@qq.com",
"mobile":"13776707007"
}
],
"user":{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
}
1、参数验证通过:
{
"code": 0,
"msg": "操作成功",
"data": {
"name": "刘志强说的都队",
"userList": [
{
"username": "王昭君",
"gender": "male",
"age": 18,
"address": "shanghai",
"email": "100001@qq.com",
"mobile": "13776707007"
}
],
"user": {
"username": "zhangsan",
"gender": "man",
"age": 18,
"address": "shanghai",
"email": "100000@qq.com",
"mobile": "13756707007"
}
}
}
测试2
2、参数验证不通过:
请求
{
"name":"刘志强说的都队",
"userList":[
{
"username":"王昭君",
"gender":null,
"age":18,
"address":"shanghai",
"email":"100001@qq.com",
"mobile":"13776707007"
}
],
"user":{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
}
响应
{
"code": 6,
"msg": "性别不能为空!",
"data": null
}
分组参数验证
将不同的校验规则分给不同的组,在使用时,指定不同的校验规则
[一个类的参数验证分为多个,可以在不同的业务场景使用不同的组进行验证,比如A组验证name、age,B组验证phone、email]
接口类
package com.zhiqiang.validated.demo.interfaces;
/**
* 校验分组1,校验规则1
*/
public interface Group1 {
}
package com.zhiqiang.validated.demo.interfaces;
/**
* 校验分组2,校验规则2
*/
public interface Group2 {
}
实体类
package com.zhiqiang.validated.demo.entity.dto;
import com.zhiqiang.validated.demo.interfaces.Group1;
import com.zhiqiang.validated.demo.interfaces.Group2;
import lombok.Data;
import javax.validation.constraints.*;
@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.zhiqiang.validated.demo.controller;
import com.zhiqiang.validated.demo.base.Result;
import com.zhiqiang.validated.demo.entity.dto.Team1Dto;
import com.zhiqiang.validated.demo.entity.dto.User2Dto;
import com.zhiqiang.validated.demo.interfaces.Group1;
import com.zhiqiang.validated.demo.interfaces.Group2;
import com.zhiqiang.validated.demo.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;
import com.zhiqiang.validated.demo.entity.dto.User1Dto;
import javax.validation.groups.Default;
/**
* @Author LZQ
* @Date 2021/9/22
**/
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
/* 测试json
{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
**/
// http://localhost:8071/api/v1/insert0
@PostMapping("/insert0")
public String validatedDemo0(@Validated @RequestBody User1Dto use1Dto){
System.out.println(use1Dto);
return "success";
}
// http://localhost:8071/api/v1/insert
@PostMapping("/insert")
public Result validatedDemo1(@Validated @RequestBody User1Dto use1Dto){
return ResultUtil.success(use1Dto);
}
/* 测试json
{
"name":"刘志强说的都队",
"userList":[
{
"username":"王昭君",
"gender":"male",
"age":18,
"address":"shanghai",
"email":"100001@qq.com",
"mobile":"13776707007"
}
],
"user":{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
}
**/
// http://localhost:8071/api/v1/insert2
@PostMapping("/insert2")
public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto) {
return ResultUtil.success(team1Dto);
}
// http://localhost:8071/api/v1/insert3
@PostMapping("/insert3")
public Result validatedDemo3(@Validated @RequestBody User2Dto user2Dto){
return ResultUtil.success(user2Dto);
}
// http://localhost:8071/api/v1/insert31
@PostMapping("/insert31")
public Result validatedDemo31(@Validated(Default.class) @RequestBody User2Dto user2Dto){
return ResultUtil.success(user2Dto);
}
// http://localhost:8071/api/v1/insert4
@PostMapping("/insert4")
public Result validatedDemo4(@Validated(Group1.class) @RequestBody User2Dto user2Dto){
return ResultUtil.success(user2Dto);
}
// http://localhost:8071/api/v1/insert5
@PostMapping("/insert5")
public Result validatedDemo5(@Validated(Group2.class) @RequestBody User2Dto user2Dto){
return ResultUtil.success(user2Dto);
}
}
测试
1、未分组校验通过:
http://localhost:8071/api/v1/insert3
请求
{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应
{
"code": 0,
"msg": "操作成功",
"data": {
"username": "zhangsan",
"gender": "man",
"age": 18,
"address": "shanghai",
"email": "100000@qq.com",
"mobile": "13756707007"
}
}
2、未分组参数校验不通过:
请求
{
"username":"zhangsan",
"gender":null,
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应
{
"code": 6,
"msg": "性别不能为空!",
"data": null
}
3、分组1(Group1.class)参数校验通过
http://localhost:8071/api/v1/insert4
请求
{
"username":"zhangsan",
"gender":"man",
"age":18,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应
{
"code": 0,
"msg": "操作成功",
"data": {
"username": "zhangsan",
"gender": "man",
"age": 18,
"address": "shanghai",
"email": "100000@qq.com",
"mobile": "13756707007"
}
}
4、分组1(Group1.class)参数校验不通过
请求1
{
"username":"",
"gender":"man",
"age":null,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应1
{
"code": 6,
"msg": "用户名不能为空!年龄有误!",
"data": null
}
请求2
{
"username":"zhangsan",
"gender":"man",
"age":0,
"address":"shanghai",
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应2
{
"code": 6,
"msg": "年龄有误!",
"data": null
}
5、分组2(Group2.class)参数校验通过
http://localhost:8071/api/v1/insert5
请求
{
"username":null,
"gender":null,
"age":null,
"address":null,
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应
{
"code": 0,
"msg": "操作成功",
"data": {
"username": null,
"gender": null,
"age": 0,
"address": null,
"email": "100000@qq.com",
"mobile": "13756707007"
}
}
6、分组2(Group2.class)参数校验不通过
请求1
{
"username":null,
"gender":null,
"age":null,
"address":null,
"email":"100000@qq.",
"mobile":"1375670700337"
}
响应1
{
"code": 6,
"msg": "邮箱有误!手机号码有误!",
"data": null
}
请求2
{
"username":null,
"gender":null,
"age":125,
"address":null,
"email":"100000@qq.com",
"mobile":"13756707007"
}
响应2
{
"code": 6,
"msg": "年龄有误!",
"data": null
}
7、使用默认分组(Default.class),参数校验通过:
说明:将控制层/insert3接口调整如下后测试
// http://localhost:8071/api/v1/insert31
@PostMapping("/insert31")
public Result validatedDemo31(@Validated(Default.class) @RequestBody User2Dto user2Dto){
return ResultUtil.success(user2Dto);
}
Default.class为Validated依赖中含有的接口类,非自定义接口类
请求
{
"username":null,
"gender":"man",
"age":null,
"address":"shanghai",
"email":null,
"mobile":null
}
响应
{
"code": 0,
"msg": "操作成功",
"data": {
"username": null,
"gender": "man",
"age": 0,
"address": "shanghai",
"email": null,
"mobile": null
}
}
8、使用默认分组,参数校验不通过:
说明:同第7点相同操作
请求
{
"username":null,
"gender":null,
"age":null,
"address":null,
"email":null,
"mobile":null
}
响应
{
"code": 6,
"msg": "地址不能为空!性别不能为空!",
"data": null
}
@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/alanzliu/demo2/tree/master/validated_demo
标签:username,Validated,区别,gender,Valid,import,null,com,email 来源: https://blog.csdn.net/qq_34254090/article/details/120433297