測試開發(fā)專題:spring-boot自定義返回參數(shù)校驗錯誤信息

之前兩篇文章 Spring-boot自定義參數(shù)校驗注解如何在spring-boot中進(jìn)行參數(shù)校驗,我們介紹了,參數(shù)校驗以及如何自定義參數(shù)校驗注解,但是當(dāng)傳遞參數(shù)出錯時,只是把錯誤信息打印到了控制臺,合理的做法是應(yīng)該把校驗的錯誤信息返回給前端,告知用戶那里有問題,下面就這一步內(nèi)容進(jìn)行說明。

請求body參數(shù)

上篇文章 Spring-boot自定義參數(shù)校驗注解的最后,在控制臺打印了校驗出錯的信息

出錯的異常類是MethodArgumentNotValidException,那如果想要自定義異常的返回,就需要在全局的異常處理器中針對這種異常進(jìn)行處理。

在這篇文章 spring-boot自定義異常返回中,我們說了如何進(jìn)行自定義異常的返回,參數(shù)校驗的錯誤信息返回依然按照此方式進(jìn)行處理,在全局異常處理類中定義異常處理方法:

@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<ObjectError> errors = ex.getBindingResult().getAllErrors();
    return UnifyResponse.builder()
            .code(5000)
            .message(formatError(errors))
            .requestUri(method + " " + requestUri)
            .build();
}

private String formatError(List<ObjectError> errors) {
    StringBuilder builder = new StringBuilder();
    errors.forEach(error -> builder.append(error.getDefaultMessage()).append(";"));
    return builder.toString();
}

我們來對上面的代碼進(jìn)行一下解釋:

  • 因為這個處理方法只是針對MethodArgumentNotValidException這個異常進(jìn)行處理,所以@ExceptionHandler(value = MethodArgumentNotValidException.class)這里指定
  • @ResponseStatus(HttpStatus.BAD_REQUEST),所有的參數(shù)校驗錯誤都是一類的,狀態(tài)碼設(shè)置為HttpStatus.BAD_REQUEST,也就是code等于400,當(dāng)然也可以定義為其他的,按照自己業(yè)務(wù)需求定義就好,可以參考這篇文章 spring-boot自定義異常返回里關(guān)于自定義狀態(tài)碼的部分。
  • @ResponseBody,因為這個異常處理方法要返回自定義的對象,所以要使用這個注解,不然spring-boot是不會對自定義對象進(jìn)行序列化的
  • List<ObjectError> errors = ex.getBindingResult().getAllErrors()進(jìn)行參數(shù)校驗的時候,可能多個參數(shù)都有問題,我們希望能夠有問題的參數(shù)的錯誤信息全部都返回回去,所以這里要獲取所有的錯誤。

回顧一下參數(shù)的定義,對這里有疑惑的同學(xué)可以看一下這篇文章Spring-boot自定義參數(shù)校驗注解

@Builder
@Getter
@Setter
@PasswordEqual(min = 5, message = "密碼和確認(rèn)密碼不一樣")
public class UserDto {

    private int userId;

    @Length(min = 2, max = 10, message = "用戶名長度必須在2-10的范圍內(nèi)")
    private String username;

    private String password;

    private String confirmPassword;
}

接下來我們定再定義一個簡單的接口,當(dāng)傳參出錯時看異常處理方法能否按照定義的那樣返回錯誤信息

@RequestMapping("/v2/user/create")
public UserDto createUser(@RequestBody @Validated  UserDto userDto){
    return userDto;
}

我們先來構(gòu)造一個密碼和確認(rèn)密碼不一致的情況

file

可以看到定義的錯誤信息被返回,而且狀態(tài)碼和自定義的code都是符合設(shè)計的,接下來我們再看一下多個參數(shù)錯誤的場景:

file

上面的場景中,用戶名是不符合要求的,密碼和確認(rèn)密碼也不一樣,所以會產(chǎn)生兩條錯誤信息,將其拼接到一起,返回給前端。

之前討論的都是body里提交的參數(shù),接下來我們看下路徑參數(shù)或者查詢參數(shù)校驗出錯時的處理

查詢參數(shù)和路徑參數(shù)

我們先定義兩個接口一個是路徑參數(shù)查詢信息,一個是通過查詢參數(shù)查詢信息

@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();
}

然后我們訪問這兩接口,當(dāng)發(fā)生錯誤時,看看他們會不會進(jìn)入上文定義的異常處理方法中:

file

很明顯,并沒有進(jìn)入上文定義的異常處理方法中,而是進(jìn)入了handleException這個異常方法當(dāng)中,這個算是個兜底的異常處理方法。

看一下控制臺的輸出:

file

這里拋出了ConstraintViolationException異常,這個異常我們并沒有定制對應(yīng)的異常處理函數(shù),下面我們就來寫一下:

@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public UnifyResponse handlerConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex){
    String requestUri = request.getRequestURI();
    String method = request.getMethod();

    Set<ConstraintViolation<?>> errors = ex.getConstraintViolations();
    return UnifyResponse.builder()
            .code(6000)
            .message(formatConstraintException(errors))
            .requestUri(method + " " + requestUri)
            .build();
}

private String formatConstraintException(Set<ConstraintViolation<?>> constraintViolations){
    StringBuilder builder = new StringBuilder();
    constraintViolations.forEach(constraintViolation -> builder.append(constraintViolation.getMessage()));
    return builder.toString();
}

整體來說異常處理和上文幾乎是一樣的,只是獲取錯誤message的方式不一樣而已,我們再請求一下:

file

至此參數(shù)校驗的錯誤message自定義返回,都完成了。

本文鏈接:https://www.immortalp.com/articles/2020/05/16/1589623786527.html
歡迎大家去 我的博客 瞅瞅,里面有更多關(guān)于測試實戰(zhàn)的內(nèi)容哦??!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容