Valid注解

news/2024/11/15 17:03:13/文章来源:https://www.cnblogs.com/fanwenyan/p/18384460

 文章链接地址:https://blog.csdn.net/m0_58680865/article/details/127817779

文章目录

  • 前言
  • 一、@Valid注解
    • 1、源码解析
    • 2、所属的包
    • 3、参数校验使用注解
      • (1)空校验
      • (2)Boolean校验
      • (3)长度校验
      • (4)日期校验
      • (5)数值校验
      • (6)其他校验
    • 4、具体使用
      • 使用 @Valid 进行参数效验步骤:
      • 运行流程:
      • 代码实践:
        • (1)添加maven依赖(三种方式添加依赖)
        • (2)创建request实体类
        • (3)创建controller
        • (4)postman测试
    • 5、异常处理
    • 6、springboot项目中的异常处理
      • (1)request实体类
      • (2)结果返回实体类
      • (3)controller接口方法
      • (4)postman测试
      • (5)全局异常处理类各种形式
          • 方式一:
          • 方式二:
  • 二、@Validated注解
      • 1、@Validated 和 @Valid 区别
      • 2、为何要分组校验?
      • 3、代码实操
        • (1)创建分组接口
        • (2)Request实体类
        • (3)controller接口
        • (4)postman测试
        • (5)注意事项!!!
  • 三、自定义校验注解
    • 业务场景
    • 自定义注解实现过程
      • (1)编写自定义注解
      • (2)自定义校验器
      • (3)编写配置文件
      • (4)postman测试

 

前言

在Javaweb的开发中,为了防止懂技术的人对数据库的恶意攻击,我们通常使用参数校验对无效数据进行筛选,Java生态下的@valid注解配
置SpringBoot的使用可以方便快速的完成对数据校验的各种场景。同时数据校验分为前端校验后端校验

可为何前端做完校验之后,还要在后端进行校验?

如果有人拿到了url地址,使用第三方测试工具比如postman就可以跳过前端页面的参数检验,所以为了数据库数据正确性,我们十分有必要对传来的数据在后端进行第二次校验

一、@Valid注解

1、源码解析

通过源码可以看出:

@Valid注解可以作用于:方法、属性(包括枚举中的常量)、构造函数、方法的形参上。

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

关于注解源码解析,可以参考如下链接:

Java如何自定义注解

2、所属的包

import javax.validation.Valid;
 
  • 1

3、参数校验使用注解

(1)空校验

注解应用
@Null 用于基本类型上,限制只能为null
@NotNull 用在基本类型上;不能为null,但可以为empty,没有Size的约束
@NotEmpty 用在集合类上面;不能为null,而且长度必须大于0
@NotBlank 只能作用在String上,不能为null,而且调用trim()后,长度必须大于0

插播一条小内容!!!null和empty有何区别?

String a = new String
String b = ""
String c = null
 
  • 1
  • 2
  • 3
  1. 此时a是分配了内存空间,但值为空,是绝对的空,是一种有值(值存在为空而已)
  2. 此时b是分配了内存空间,值为空字符串,是相对的空,是一种有值(值存在为空字串)
  3. 此时c是未分配内存空间,无值,是一种无值(值不存在)

(2)Boolean校验

注解应用
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true

(3)长度校验

注解应用
@Size(max,min) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) 验证字符串长度是否在给定的范围之内

(4)日期校验

注解应用
@Past 限制必须是一个过去的日期,并且类型为java.util.Date
@Future 限制必须是一个将来的日期,并且类型为java.util.Date
@Pattern(value) 限制必须符合指定的正则表达式

(5)数值校验

建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null。

注解应用
@Min(value) 验证 Number 和 String 对象必须为一个不小于指定值的数字
@Max(value) 验证 Number 和 String 对象必须为一个不大于指定值的数字
@DecimalMax(value) 限制必须为一个不大于指定值的数字,小数存在精度
@DecimalMin(value) 限制必须为一个不小于指定值的数字,小数存在精度
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Digits 验证 Number 和 String 的构成是否合法
@Range(max =3 , min =1 , message = " ") Checks whether the annotated value lies between (inclusive) the specified minimum and maximum

Max和Min是对你填的“数字”是否大于或小于指定值,这个“数字”可以是number或者string类型。长度限制用length。

(6)其他校验

注解应用
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email

4、具体使用

使用 @Valid 进行参数效验步骤:

  1. 实体类中添加 @Valid 相关注解
  2. 接口类中添加 @Valid 注解
  3. 全局异常处理类中处理 @Valid 抛出的异常

运行流程:

整个过程如下图所示,用户访问接口,然后进行参数效验,因为 @Valid 不支持平面的参数效验(直接写在参数中字段的效验)所以基于 GET 请求的参数还是按照原先方式进行效验,而 POST 则可以以实体对象为参数,可以使用 @Valid 方式进行效验。如果效验通过,则进入业务逻辑,否则抛出异常,交由全局异常处理器进行处理。

在这里插入图片描述

代码实践:

(1)添加maven依赖(三种方式添加依赖)
			<!--第一种:valid依赖--><dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>版本号</version></dependency><!--		第二种:集成于web依赖中(注意版本号)--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.0.5.RELEASE</version></dependency><!--		第三种:springboot的validation--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
(2)创建request实体类
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;@Data
@NoArgsConstructor
public class TestRequest {@NotBlank(message = "name不为空")private String name;@Length(max = 3,message = "address最大长度是3")private String address;@Max(value = 5,message = "reqNo最大值是5")private String reqNo;}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
(3)创建controller
@RestController
public class ValidTestController {@RequestMapping("/valid/test")public void test(@Valid @RequestBody TestRequest request){System.out.println(request);}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(4)postman测试

在这里插入图片描述

postman返回结果:

{"timestamp": "2022-11-12T09:54:24.202+00:00","status": 400,"error": "Bad Request","trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.example.controller.ValidTestController.test(com.example.domain.TestRequest) with 3 errors: [Field error in object 'testRequest' on field 'name': rejected value []; codes [NotBlank.testRequest.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testRequest.name,name]; arguments []; default message [name]]; default message [name不为空]] [Field error in object 'testRequest' on field 'reqNo': rejected value [8]; codes [Max.testRequest.reqNo,Max.reqNo,Max.java.lang.String,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testRequest.reqNo,reqNo]; arguments []; default message [reqNo],5]; default message [reqNo最大值是5]] [Field error in object 'testRequest' on field 'address': rejected value [gtyjh]; codes [Length.testRequest.address,Length.address,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [testRequest.address,address]; arguments []; default message [address],3,0]; default message [address最大长度是3]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:141)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:681)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n","message": "Validation failed for object='testRequest'. Error count: 3","errors": [{"codes": ["NotBlank.testRequest.name","NotBlank.name","NotBlank.java.lang.String","NotBlank"],"arguments": [{"codes": ["testRequest.name","name"],"arguments": null,"defaultMessage": "name","code": "name"}],"defaultMessage": "name不为空","objectName": "testRequest","field": "name","rejectedValue": "","bindingFailure": false,"code": "NotBlank"},{"codes": ["Max.testRequest.reqNo","Max.reqNo","Max.java.lang.String","Max"],"arguments": [{"codes": ["testRequest.reqNo","reqNo"],"arguments": null,"defaultMessage": "reqNo","code": "reqNo"},5],"defaultMessage": "reqNo最大值是5","objectName": "testRequest","field": "reqNo","rejectedValue": "8","bindingFailure": false,"code": "Max"},{"codes": ["Length.testRequest.address","Length.address","Length.java.lang.String","Length"],"arguments": [{"codes": ["testRequest.address","address"],"arguments": null,"defaultMessage": "address","code": "address"},3,0],"defaultMessage": "address最大长度是3","objectName": "testRequest","field": "address","rejectedValue": "gtyjh","bindingFailure": false,"code": "Length"}],"path": "/valid/test"
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

从后端返回给postman的结果可以看出,三个字段的校验都已经实现。但是,特别情况,RequestBody可能是嵌套的实体,这个时候,对于嵌套的实体类来说,嵌套必须加 @Valid,如果只在字段上添加校验注解嵌套中的验证不生效。

如果只在嵌套类字段上加上校验注解,如下:

import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;@Data
@NoArgsConstructor
public class TestRequest {@NotBlank(message = "name不为空")private String name;@Length(max = 3, message = "address最大长度是3")private String address;@Max(value = 5, message = "reqNo最大值是5")private String reqNo;private TestRequestInner inner;@Data@NoArgsConstructorpublic static class TestRequestInner {@Length(max = 3, message = "最大长度是3")private String sonName;private Integer sonAge;private String schoolNo;}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

postman测试:
在这里插入图片描述

控制台打印:
在这里插入图片描述
可以看出,嵌套类中sonName的长度校验并没有起到作用。

在嵌套类的外层加上@Valid注解,如下:

@Data
@NoArgsConstructor
public class TestRequest {@NotBlank(message = "name不为空")private String name;@Length(max = 3, message = "address最大长度是3")private String address;@Max(value = 5, message = "reqNo最大值是5")private String reqNo;@Validprivate TestRequestInner inner;//   即使放在list集合里面仍然是需要加上 @Valid 注解
//    @Valid
//    private List<TestRequestInner> inner;@Data@NoArgsConstructorpublic static class TestRequestInner {@Length(max = 3, message = "最大长度是3")private String sonName;private Integer sonAge;private String schoolNo;}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

postman测试:

校验成功。

在这里插入图片描述

5、异常处理

刚才的测试我们看到,校验注解全部生效,但是所有的异常全部抛出给postman,从控制台可以看出:
在这里插入图片描述
程序抛出了MethodArgumentNotValidException异常信息,在实际业务中,有时候需要处理这个异常,这个时候就需要一个全局异常处理类中处理 @Valid 抛出的异常。

抛出的异常结构:

在这里插入图片描述

代码如下:

实体类如上不变,controller接口方法改为返回string:

 @RequestMapping("/valid/test")public String test(@Valid @RequestBody TestRequest request){System.out.println(request);return "success";}
 
  • 1
  • 2
  • 3
  • 4
  • 5

异常处理类:

我们可以根据上图抛出的异常结构,去get我们想要获得的内容。

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.List;@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {/*** 自定义验证异常* MethodArgumentNotValidException 方法参数无效异常*/@ResponseStatus(HttpStatus.BAD_REQUEST) //设置状态码为 400@ExceptionHandler({MethodArgumentNotValidException.class})public String paramExceptionHandler(MethodArgumentNotValidException e) {BindingResult exceptions = e.getBindingResult();
// 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息if (exceptions.hasErrors()) {List errors = exceptions.getAllErrors();if (!errors.isEmpty()) {
// 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可FieldError fieldError = (FieldError) errors.get(0);return fieldError.getDefaultMessage();}}return "请求参数错误";}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

postman测试:

在这里插入图片描述

6、springboot项目中的异常处理

上述的异常处理只是一个简单的string返回,但是在实际项目中,返回结构是固定的,下面对于固定的返回结构,做异常处理。

(1)request实体类

import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import java.util.List;@Data
@NoArgsConstructor
public class TestRequest {@NotBlank(message = "name不为空")private String name;@Length(max = 3, message = "address最大长度是3")private String address;@Max(value = 5, message = "reqNo最大值是5")private String reqNo;@Validprivate List<TestRequestInner> inner;@Data@NoArgsConstructorpublic static class TestRequestInner {@Length(max = 3, message = "最大长度是3")private String sonName;private Integer sonAge;@NotBlank(message = "schoolNo不空")private String schoolNo;}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

(2)结果返回实体类

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class ResponseResult {private List<ProvideInfo> provideInfos;@Data@NoArgsConstructor@Builder@AllArgsConstructorpublic static class ProvideInfo {private String code;private String detail;}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

(3)controller接口方法

import com.example.domain.ResponseResult;
import com.example.domain.TestRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@RestController
public class ValidTestController {@RequestMapping("/valid/test")public ResponseEntity<ResponseResult> test(@Valid @RequestBody TestRequest request) {System.out.println(request);return null;}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(4)postman测试

在这里插入图片描述

再插播一个小插曲!!!!

刚开始写的结果返回实体类中的provideInfo是这样式儿滴:
在这里插入图片描述
然后运行项目就出了这个错:

在这里插入图片描述
英文版是这样式儿滴:

在这里插入图片描述

上网查了一下,问题出现在这:

在这里插入图片描述

原因:

本应当(只能)使用无参构造器,但编译器却发现代码中使用了有参(全参)构造器,这个全参构造器出现在Builder类的build()方法中,该方法试图调用一个全参构造器。这个报错信息表明,@NoArgsConstructor抑制了@Builder生成全参构造器,只生成了一个无参构造器,使用lombok插件delombok @Builder@NoArgsConstructor两个注解,可以证实这一抑制现象。

解决方法:一种方法是同时使用@Builder、@NoArgsConstructor和@AllArgConstructor,还有一种方法是显式添加@Tolerate注解的无参构造器。

然后在provideInfo上添加了@AllArgConstructor注解(如下图),成功运行!
在这里插入图片描述

(5)全局异常处理类各种形式

对于全局异常类的处理,涉及到拦截器相关内容,这里不做多说。全局异常类的处理方式有很多种:

方式一:
// Enum枚举类
public enum CodeEnum {
// 根据自己的项目需求更改状态码,这里只是一个示范UNKNOW_EXCEPTION(10000,"系统未知错误"),VALID_EXCETIPON(10001,"参数格式校验错误");private int code;private String msg;CodeEnum(int code, String msg){this.code = code;this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}//异常处理类
@Slf4j
@RestControllerAdvice("com.cbj.db_work.controller") //表明需要处理异常的范围
public class GlobalExceptionHandler {@ExceptionHandler(value = MethodArgumentNotValidException.class) // 参数异常抛出的异常类型为MethodArgumentNotValidException,这里捕获这个异常// R为统一返回的处理类public R validExceptionHandler(MethodArgumentNotValidException e){System.out.println("数据异常处理");log.error("数据校验出现问题,异常类型:{}",e.getMessage(),e.getClass());BindingResult bindingResult = e.getBindingResult();Map<String,String> map = new HashMap<>();bindingResult.getFieldErrors().forEach((item)->{String message = item.getDefaultMessage();// 获取错误的属性字段名String field = item.getField();map.put(field,message);});return R.error().code(CodeEnum.VALID_EXCETIPON.getCode()).message(CodeEnum.VALID_EXCETIPON.getMsg()).data("errorData",map);}}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
方式二:
@ControllerAdvice
@RestControllerAdvice
@Slf4j
public class ValidExceptionHandler extends GlobalExceptionHandler {// GET请求参数异常处理@ExceptionHandler(value = ConstraintViolationException.class)public Result<Object> constraintViolationExceptionHandler(ConstraintViolationException e) {StringBuilder msg = new StringBuilder();Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();for (ConstraintViolation<?> constraintViolation : constraintViolations) {String message = constraintViolation.getMessage();msg.append(message).append(";");}return ResultResponse.getFailResult(ResultCode.BODY_NOT_MATCH.getResultCode(), msg.toString());}@ExceptionHandler(ArithmeticException.class)public Result<Object> arithmeticExceptionHandler(ArithmeticException e) {e.printStackTrace();return ResultResponse.getFailResult(ResultCode.NOT_FOUND.getResultCode(), "算术异常!"+e.getMessage());}// POST请求参数异常处理@ExceptionHandler(BindException.class)public Result<Object> bindExceptionHandler(BindException e) {FieldError fieldError = e.getBindingResult().getFieldError();String msg;if (Objects.isNull(fieldError)) {msg = "POST请求参数异常:" + JSON.toJSONString(e.getBindingResult());log.info(msg);} else {msg = fieldError.getDefaultMessage();}return ResultResponse.getFailResult(ResultCode.BODY_NOT_MATCH.getResultCode(), msg);}}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

特别的,get请求校验:

@RestController
@RequestMapping(value = "/test")
@Slf4j
//@ApiIgnore
@Validated
public class TestController {@GetMapping(value = "/test")public Result<Object> test(@NotNull(message = "name必传")@NotBlank(message = "name格式错误")String name) {return ResultResponse.getSuccessResult("hello: " + name);}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

二、@Validated注解

1、@Validated 和 @Valid 区别

  1. @Validate 是对@Valid 的封装
  2. @Validate 可以进行分组验证 ,一个对象中都写了验证而你只需要验证该方法需要的验证时使用

2、为何要分组校验?

假设有这样一种场景:

我们使用同一个VO(Request)类来传递save和update方法的数据,但对于id来说,通常有框架帮我们生成id,我们不需要传id此时需要使用注解@Null表名id数据必须为空。但对于update方法,我们必须传id才能进行update操作,所以同一个字段面对不同的场景不同需求就可以使用分组校验

3、代码实操

(1)创建分组接口

这里并不需要实现编写什么代码,标明分类。

//分组接口 1
public interface InsertGroup {
}//分组接口 2
public class UpdateGroup {
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(2)Request实体类
@Data
@NoArgsConstructor
public class TestRequest {@Null(message = "无需传id",groups = InsertGroup.class)@NotBlank(message = "必须传入id",groups = UpdateGroup.class)private String id;}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
(3)controller接口
	@RequestMapping("/valid/test")public ResponseEntity<ResponseResult> test(@Validated({UpdateGroup.class})@RequestBody TestRequest request) {System.out.println(request);return null;}
 
  • 1
  • 2
  • 3
  • 4
  • 5
(4)postman测试

在这里插入图片描述

(5)注意事项!!!

当我们在controller层指定分组后,属性上没有表名分组的校验还执行么?

下面的Request实体中,id进行了分组,address没有进行分组:

@Data
@NoArgsConstructor
public class TestRequest {@Null(message = "无需传id",groups = InsertGroup.class)@NotBlank(message = "必须传入id",groups = UpdateGroup.class)private String id;@Length(max = 3, message = "address最大长度是3")private String address;}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

postman测试:

如下图,校验没有成功!!!!说明一旦开启了分组校验,就必须把所有的校验规则都指定组别,不然不生效

在这里插入图片描述

三、自定义校验注解

业务场景

假设我们有一个字段比如showStatus只能由0和1两个取值,我们可以使用正则,也可以自定义注解校验,这里我们展示如何使用自定义校验.

如下图所示:

在这里插入图片描述

自定义注解实现过程

(1)编写自定义注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;// Target表示注解使用的范围
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
// 获取注解的时间,固定值
@Retention(RetentionPolicy.RUNTIME)
// 匹配的校验器,我们稍后编写,关联注解和校验器
@Constraint(validatedBy = {StatusValueValidator.class})
@Documented
public @interface StatusValue {// 错误信息去哪找,通常我们使用配置文件,稍后编写String message() default "{com.cbj.db_work.valid.ListValue.message}";// 支持分组校验Class<?>[] groups() default {};// 自定义负载信息Class<? extends Payload>[] payload() default {};// 指定参数,就是上图中指定的可取值的范围int []  vals() default {};
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

(2)自定义校验器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;// 实现ConstraintValidator接口,泛型值:<自定义注解类,被校验值的数据类型>
public class StatusValueValidator implements ConstraintValidator<StatusValue, Integer> {// 整体思路,使用set在initialize获得参数信息,在isValid方法中校验,成功true,失败falseSet<Integer> set = new HashSet<>();// 初始化方法,可以得到详细信息@Overridepublic void initialize(StatusValue constraintAnnotation) {int[] vals = constraintAnnotation.vals();for (int val : vals) {set.add(val);}}/*** 校验是否匹配* @param value 就是需要校验的值* @param constraintValidatorContext* @return*/@Overridepublic boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {return set.contains(integer);}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

(3)编写配置文件

在这里插入图片描述

com.cbj.db_work.valid.ListValue.message=必须提交指定的值
 
  • 1

在配置文件中可以指定匹配错误时显示的信息,也可以message指定。

在这里插入图片描述

(4)postman测试

在这里插入图片描述

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ACMMM2024】Multi-Scale and Detail-Enhanced Segment Anything Model for Salient Object Detection

论文:https://arxiv.org/pdf/2408.04326 代码:https://github.com/BellyBeauty/MDSAM论文的研究动机就是使用SAM来解决显著性检测(SOD)问题,主要有两个改进:提出了Lightweight Multi-Scale Adapter, LMSA来微调SAM 提出了Multi-Level Fusion Module, MLFM 和 Detail Enha…

Amazon Bedrock 实践:零基础创建贪吃蛇游戏

本文探讨了如何利用 Amazon Bedrock 和大型语言模型,快速创建经典的贪吃蛇游戏原型代码。重点展示了利用提示工程,将创新想法高效转化为可运行代码方面的过程。文章还介绍了评估和优化提示词质量的最佳实践。亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有…

题解:P11000 [蓝桥杯 2024 省 Python B] 数字串个数

P1100,纪念这个特别的数字,来水一篇。用 \(1 \sim 9\) 没有任何特殊情况的方法数:\(9^{10000}\)。 排除没有 \(3\) 和 \(7\) 的方法。 \(9^{10000} - 8^{10000} - 8^{10000}\) 加上 \(3\) 和 \(7\) 混一起的方法数。 \(9^{10000} - {(9 - 1)}^{10000} - {(9 - 1)}^{10000} +…

【音视频通话】使用asp.net core 8+vue3 实现高效音视频通话

引言在三年前,写智能小车的时候,当时小车上有一个摄像头需要采集,实现推拉流的操作,技术选型当时第一版用的是nginx的rtmp的推拉流,服务器的配置环境是centos,2H4G3M的一个配置,nginx的rtmp的延迟是20秒,超慢,后来研究了SRS以及ZLMediaKit这两个开源的推拉流服务器,没…

免费、开源、详细完整的unity游戏、游戏源码、教程:人工智能分析和处理对话的美好三维世界(定期更新)

免费、开源、详细完整的unity游戏、游戏源码、教程:人工智能分析和处理对话的美好三维世界。这份unity游戏、游戏源码、教程:完全免费,完全开源,完整详细,通俗易懂,适合初学者入门,定期更新。 我不想和任何人说话,任何人不要跟我说话,不要打扰我,我要安安静静的写。我…

小企业必备:优选局域网文档管理软件推荐

国内外主流的10款局域网文档管理软件对比:1.PingCode;2.Worktile;3.语雀;4.联想Filez企业网盘;5.亿方云;6.黑域基地;7.Joplin;8.MediaWiki;9.TiddlyWiki;10.Zim Wiki。在处理局域网文档时,企业常常面临着文件管理不系统、数据安全性差等问题,这不仅影响团队协作效率…

机器学习之——决策树信息熵计算[附加计算程序]

0 前言本文主要讲述了决策树背后的信息熵的公式含义及计算方式,并列举出多道例题帮助理解。1 信息熵的定义 1.1 信息熵公式笔者使用下图(1-1)直观理解信息熵的含义。信息熵越大,表示该随机变量的不确定性越高。对于均匀分布,信息熵达到最大值。 1.2 证明:对于均匀分布,信息…

phpinclude-labs做题记录

Level 1 file协议payload:?wrappers=/flag Level 2 data协议 去包含data协议中的内容其实相当于进行了一次远程包含,所以data协议的利用条件需要 php.ini 中开启 allow_url_fopen 和 allow_url_include GET: ?wrappers=, 然后 POST:helloctf=system(cat /flag); Level 3 dat…

数据结构学习第一周

本文需要掌握的知识 1.认识数据结构 2.了解数据结构(逻辑结构)的分类 3.内存储器模型以及分配方式(物理结构) 4.认识Node类 5.简单了解泛型1 .数据结构(D-S/Data Structure) 1.1简介 1.1.1数据 分为原子数据和复合数据 1.1.2结构 分为逻辑结构和物理结构数据结构是由数据和数…

用 Higress AI 网关降低 AI 调用成本 - 阿里云天池云原生编程挑战赛参赛攻略

我们要在 Higress 网关中编写 WebAssembly(wasm)插件,使得在 http 请求的各个阶段(requestHeader,requestBody,responseHeader,responseBody)能够将相应的请求或返回捕获进行业务逻辑的处理。具体到本比赛,主要需要实现的是缓存对大模型的请求(openai 接口的形式)在…

科研项目管理工具选型全攻略

国内外主流的 10 款科研院所项目管理系统对比:PingCode、Worktile、云效、Tower 、Zoho Projects、Notion、Wrike、ClickUp、Asana、Teambition。在科研院所的日常运营中,项目管理系统的选择显得尤为重要。选择不当可能导致资源浪费、进度延误甚至项目失败,这是每个科研团队…

ensp使用交换机配置svi连通网段

ensp使用交换机配置svi连通网段 实验目的 如下图所示,PC1、PC2、PC3分别位于不同网段,使用S5700型号交换机连接,目前需要配置交换机和主机,主机能够互相连通。常用命令un in en:关闭信息通知 dis ip int b:显示端口ip配置情况(brief模式) dis ip routing-table:显示路…