springboot中@Valid注解與@Validated注解區別以及全局異常的處理


前端傳過來數據的時候,要進行校驗,但是大量的校驗很繁瑣,會造成大量的if else語句的產生,所以@Valid和@Validated很好的解決了這個問題.

首先說一下兩個注解的區別:

  1.兩者的所屬的包是不同的

    @Valid屬於javax.validation包下,是jdk給提供的

    @Validated是org.springframework.validation.annotation包下的,是spring提供的

  2.@Validated要比@Valid更加強大

    @Validated在@Valid之上提供了分組功能和驗證排序功能

一.處理校驗的異常

首先定義一個實體類:

@Data public class Person { @NotEmpty(message = "姓名不能為空") private String name; @Max(value = 18,message = "年齡不能超過18歲") private String age; @Max(value = 1, message = "性別只能為0和1: 0=女1=男") @Min(value = 0, message = "性別只能為0和1: 0=女1=男") private Short sex; }

然后controller,BindingResult對象,用於獲取校驗失敗情況下的反饋信息:

@RestController @Slf4j public class VerifyController { @PostMapping(value = "/valid") public void verifyValid(@Valid @RequestBody Person person, BindingResult result) { log.info("I am verifyValid() method, the request params is: 【{}】", JSON.toJSONString(person)); if (result.hasErrors()) { FieldError fieldError = result.getFieldError(); if (fieldError != null) { log.error("error msg: 【{}】", fieldError.getDefaultMessage()); } } } @PostMapping(value = "/validated") public void verifyValidated(@Validated @RequestBody Person person, BindingResult result) { log.info("I am verifyValidated() method, the request params is: 【{}】", JSON.toJSONString(person)); if (result.hasErrors()) { FieldError fieldError = result.getFieldError(); if (fieldError != null) { log.error("error msg: 【{}】", fieldError.getDefaultMessage()); } } } }

此時訪問兩個controller結果都可以進行校驗:

/validated:

2020-01-14 12:32:54.805  INFO 25244 --- [nio-8080-exec-1] com.example.controller.VerifyController  : I am verifyValidated() method, the request params is: 【{"age":"19","name":"","sex":10}】 2020-01-14 12:32:54.806 ERROR 25244 --- [nio-8080-exec-1] com.example.controller.VerifyController  : error msg: 【性別只能為0和1: 0=女1=男】 2020-01-14 12:33:10.638  INFO 25244 --- [nio-8080-exec-2] com.example.controller.VerifyController  : I am verifyValidated() method, the request params is: 【{"age":"19","name":"","sex":1}】 2020-01-14 12:33:10.639 ERROR 25244 --- [nio-8080-exec-2] com.example.controller.VerifyController : error msg: 【年齡不能超過18歲】 2020-01-14 12:33:18.034  INFO 25244 --- [nio-8080-exec-3] com.example.controller.VerifyController  : I am verifyValidated() method, the request params is: 【{"age":"18","name":"","sex":1}】 2020-01-14 12:33:18.034 ERROR 25244 --- [nio-8080-exec-3] com.example.controller.VerifyController  : error msg: 【姓名不能為空】

/valid:

2020-01-14 12:35:19.151  INFO 25244 --- [nio-8080-exec-5] com.example.controller.VerifyController  : I am verifyValid() method, the request params is: 【{"age":"19","name":"","sex":10}】 2020-01-14 12:35:19.151 ERROR 25244 --- [nio-8080-exec-5] com.example.controller.VerifyController : error msg: 【姓名不能為空】 2020-01-14 12:35:24.306  INFO 25244 --- [nio-8080-exec-6] com.example.controller.VerifyController  : I am verifyValid() method, the request params is: 【{"age":"19","name":"aa","sex":10}】 2020-01-14 12:35:24.306 ERROR 25244 --- [nio-8080-exec-6] com.example.controller.VerifyController : error msg: 【年齡不能超過18歲】 2020-01-14 12:35:29.565  INFO 25244 --- [nio-8080-exec-7] com.example.controller.VerifyController  : I am verifyValid() method, the request params is: 【{"age":"18","name":"aa","sex":10}】 2020-01-14 12:35:29.565 ERROR 25244 --- [nio-8080-exec-7] com.example.controller.VerifyController  : error msg: 【性別只能為0和1: 0=女1=男】

在有些時候我們不一定能夠使用BindingResult result來處理校驗的結果集,在實際的生產環境中,更方便的是獲取該異常然后進行返回.

通過觀察發現,兩者在使用@RequestBody參數注解的情況下,兩者拋出的都是MethodArgumentNotValidException異常:

org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.example.controller.VerifyController.verifyValidated(com.example.model.Person) with 3 errors: [Field error in object 'person' on field 'age': rejected value [19]; codes [Max.person.age,Max.age,Max.java.lang.String,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age],18]; default message [年齡不能超過18歲]] [Field error in object 'person' on field 'name': rejected value []; codes [NotEmpty.person.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.name,name]; arguments []; default message [name]]; default message [姓名不能為空]] [Field error in object 'person' on field 'sex': rejected value [10]; codes [Max.person.sex,Max.sex,Max.java.lang.Short,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.sex,sex]; arguments []; default message [sex],1]; default message [性別只能為0和1: 0=女1=男]] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:139) at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)

為了統一抓取異常,首先定義一個全局異常類:

@ControllerAdvice @ResponseBody public class GlobleExceptionHandler { /** * 要攔截的異常Exception */ @ExceptionHandler(value = Exception.class) public Result<String> exceptionHandler(Exception e) { if (e instanceof BindException) { e.printStackTrace();  //將異常打印出來 BindException ex = (BindException) e; List<ObjectError> allErrors = ex.getAllErrors(); ObjectError objectError = allErrors.get(0); String ms = objectError.getDefaultMessage(); return Result.error(CodeMsg.BIND_ERROR.fillArgs(ms)); } else { e.printStackTrace(); return Result.error(CodeMsg.SERVER_ERROR); } } }

返回值的Result是統一封裝了返回給前端的json數據,具體的實現可以在我上一篇博客中查看:

https://www.cnblogs.com/zhiweiXiaomu/p/12190353.html

但是如果不在controller接口中加@RequestBody注解,兩者拋出的則是BindException:

 

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 3 errors

 

這里提醒一下各位小伙伴,在controller接口中加不加@ResquestBody注解,拋出的校驗異常是不一樣的.各位在抓取異常的時候需要注意一下.

二.@Valid和@Validated兩者的區別

  兩者的主要區別就是關於分組和分組排序了.@Validated中可以進行分組排序

  首先定義兩個接口:  

  

public interface First { } public interface Second { }

 

 

 

  這倆接口是用來分組的.

  實體類如下:

@Data public class Person { @NotEmpty(groups = First.class, message = "姓名不能為空") private String name; @Max(value = 18, groups = Second.class,message = "年齡不能超過18歲") private String age; @Max(value = 1, message = "性別只能為0和1: 0=女1=男") @Min(value = 0, message = "性別只能為0和1: 0=女1=男") private Short sex; }

 

  然后controller(與上面一樣,兩個都沒有添加分組),多次校驗下我們會發現不管是 @Valid 注解還是 @Validated 注解都只會校驗沒有添加 groups 屬性的實體類字段 (此處只校驗了 sex 字段)

  也就是說,在實體類中,添加分組的兩個字段,name,和age在校驗中失效了.

  因為@Valid注解中沒有關於分組的參數,所以在@Validated中加入注解,變成下面這樣:

@RestController @Slf4j public class VerifyController { @PostMapping(value = "/valid") public void verifyValid(@Valid @RequestBody Person person, BindingResult result) { log.info("I am verifyValid() method, the request params is: 【{}】", JSON.toJSONString(person)); if (result.hasErrors()) { FieldError fieldError = result.getFieldError(); if (fieldError != null) { log.error("error msg: 【{}】", fieldError.getDefaultMessage()); } } } @PostMapping(value = "/validated") public void verifyValidated(@Validated(value = First.class) @RequestBody Person person, BindingResult result) { log.info("I am verifyValidated() method, the request params is: 【{}】", JSON.toJSONString(person)); if (result.hasErrors()) { FieldError fieldError = result.getFieldError(); if (fieldError != null) { log.error("error msg: 【{}】", fieldError.getDefaultMessage()); } } }
}

 

  此時,@Validated則可以校驗對應有First分組的name字段了,但是別的字段,也就是說除了加了First分組的name字段,別的字段都不能進行校驗.

         那么怎么辦呢,因為value是Clazz[]數組格式的,所以可以這樣:

  value={First.class,Second.class}

  也可以進行分組排序:

  定義一個分組排序接口:

  

@GroupSequence({First.class,Second.class}) public interface Group { }

 

   然后controller中    value=Group.class,就可以對所有已經分組的字段進行校驗了,但是沒有進行分組的字段是不會被校驗的.

  

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM