之前兩篇文章 Spring-boot自定義參數校驗註解和如何在spring-boot中進行參數校驗,我們介紹了,參數校驗以及如何自定義參數校驗註解,但是當傳遞參數出錯時,只是把錯誤信息打印到了控制檯,合理的做法是應該把校驗的錯誤信息返回給前端,告知用戶那裡有問題,下面就這一步內容進行說明。
請求body參數
上篇文章 Spring-boot自定義參數校驗註解的最後,在控制檯打印了校驗出錯的信息
出錯的異常類是MethodArgumentNotValidException,那如果想要自定義異常的返回,就需要在全局的異常處理器中針對這種異常進行處理。
在這篇文章 spring-boot自定義異常返回中,我們說了如何進行自定義異常的返回,參數校驗的錯誤信息返回依然按照此方式進行處理,在全局異常處理類中定義異常處理方法:
<code>@ExceptionHandler(value = MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody public UnifyResponse handlerBeanValidationException(HttpServletRequest request, MethodArgumentNotValidException ex) { String requestUri = request.getRequestURI(); String method = request.getMethod(); List errors = ex.getBindingResult().getAllErrors(); return UnifyResponse.builder() .code(5000) .message(formatError(errors)) .requestUri(method + " " + requestUri) .build(); } private String formatError(List errors) { StringBuilder builder = new StringBuilder(); errors.forEach(error -> builder.append(error.getDefaultMessage()).append(";")); return builder.toString(); }/<code>
我們來對上面的代碼進行一下解釋:
- 因為這個處理方法只是針對MethodArgumentNotValidException這個異常進行處理,所以@ExceptionHandler(value = MethodArgumentNotValidException.class)這裡指定
- @ResponseStatus(HttpStatus.BAD_REQUEST),所有的參數校驗錯誤都是一類的,狀態碼設置為HttpStatus.BAD_REQUEST,也就是code等於400,當然也可以定義為其他的,按照自己業務需求定義就好,可以參考這篇文章 spring-boot自定義異常返回裡關於自定義狀態碼的部分。
- @ResponseBody,因為這個異常處理方法要返回自定義的對象,所以要使用這個註解,不然spring-boot是不會對自定義對象進行序列化的
- List errors = ex.getBindingResult().getAllErrors()進行參數校驗的時候,可能多個參數都有問題,我們希望能夠有問題的參數的錯誤信息全部都返回回去,所以這裡要獲取所有的錯誤。
回顧一下參數的定義,對這裡有疑惑的同學可以看一下這篇文章Spring-boot自定義參數校驗註解
<code>@Builder @Getter @Setter @PasswordEqual(min = 5, message = "密碼和確認密碼不一樣") public class UserDto { private int userId; @Length(min = 2, max = 10, message = "用戶名長度必須在2-10的範圍內") private String username; private String password; private String confirmPassword; }/<code>
接下來我們定再定義一個簡單的接口,當傳參出錯時看異常處理方法能否按照定義的那樣返回錯誤信息
<code>@RequestMapping("/v2/user/create") public UserDto createUser(@RequestBody @Validated UserDto userDto){ return userDto; }/<code>
我們先來構造一個密碼和確認密碼不一致的情況
可以看到定義的錯誤信息被返回,而且狀態碼和自定義的code都是符合設計的,接下來我們再看一下多個參數錯誤的場景:
上面的場景中,用戶名是不符合要求的,密碼和確認密碼也不一樣,所以會產生兩條錯誤信息,將其拼接到一起,返回給前端。
之前討論的都是body裡提交的參數,接下來我們看下路徑參數或者查詢參數校驗出錯時的處理
查詢參數和路徑參數
我們先定義兩個接口一個是路徑參數查詢信息,一個是通過查詢參數查詢信息
<code>@GetMapping("/v2/user/info") public UserDto getUserInfo(@RequestParam @Length(min = 2, max = 5, message = "用戶名長度必須在2-5的範圍") String username){ return UserDto.builder() .userId(1000) .username(username) .build(); } @GetMapping("/v2/user/{username}") public UserDto getUserInfoV2(@PathVariable @Length(min = 2, max = 5, message = "用戶名長度必須在2-5的範圍") String username){ return UserDto.builder() .userId(2000) .username(username) .build(); }/<code>
然後我們訪問這兩接口,當發生錯誤時,看看他們會不會進入上文定義的異常處理方法中:
很明顯,並沒有進入上文定義的異常處理方法中,而是進入了handleException這個異常方法當中,這個算是個兜底的異常處理方法。
看一下控制檯的輸出:
這裡拋出了ConstraintViolationException異常,這個異常我們並沒有定製對應的異常處理函數,下面我們就來寫一下:
<code>@ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody public UnifyResponse handlerConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex){ String requestUri = request.getRequestURI(); String method = request.getMethod(); Set> errors = ex.getConstraintViolations(); return UnifyResponse.builder() .code(6000) .message(formatConstraintException(errors)) .requestUri(method + " " + requestUri) .build(); } private String formatConstraintException(Set> constraintViolations){ StringBuilder builder = new StringBuilder(); constraintViolations.forEach(constraintViolation -> builder.append(constraintViolation.getMessage())); return builder.toString(); }/<code>
整體來說異常處理和上文幾乎是一樣的,只是獲取錯誤message的方式不一樣而已,我們再請求一下:
至此參數校驗的錯誤message自定義返回,都完成了。
總結
以上就是我總結的關係參數校驗出錯以後,錯誤信息返回的處理方式,目前系列文章正在不斷編寫中,包括spring-boot和vue,後面我們將以此為基礎打造一個自動化測試平臺。