最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot 異常處理/自定義格式校驗的問題實例詳解

 更新時間:2025年07月23日 14:34:41   作者:Su-RE  
文章探討Spring Boot中自定義注解校驗問題,區(qū)分參數(shù)級與類級約束觸發(fā)的異常類型,建議通過@RestControllerAdvice統(tǒng)一處理,并說明如何獲取不同錯誤類型(FieldError/ObjectError)的信息差異,感興趣的朋友一起看看吧

1. 問題簡要描述

這個問題是在測試自定義注解實現(xiàn)數(shù)據(jù)格式校驗時遇到的,當前有兩個注解,ValidateParamsMatched和SafeParam。前者的功能是對類中的兩個列表的長度進行比較,后者則是對字符串進行校驗,判斷是否包含特殊字符
但是,當分別觸發(fā)這兩個校驗時,都會觸發(fā)異常并返回請求,但之后后者能返回請求信息。
這里問題本質上是沒弄懂對應的異常處理代碼,但也值得記錄一下。

2. 異常觸發(fā)

1) 參數(shù)級別約束

在 Controller 方法上直接使用參數(shù)級約束,并且,Controller 類上有 @Validated 注解時,驗證失敗會觸發(fā) ConstraintViolationException 異常

@Validated // 必需
@RestController
public class ProductController {
    @GetMapping("/products")
    public Product getProduct(@ValidCategory String category) {
        // ...
    }
}

在 DTO 的字段上使用參數(shù)級約束,且方法參數(shù)前有 @Valid 注解時,驗證失敗會觸發(fā) MethodArgumentNotValidException 異常

@PostMapping("/products")
public ResponseEntity<?> createProduct(
    @Valid @RequestBody ProductRequest request // 觸發(fā)驗證
) {
    // ...
}

2) 類級別約束

在實體類上標注了類級約束,且Controller 方法參數(shù)使用了 @Valid,觸發(fā)MethodArgumentNotValidException

@ValidInventory // 類級別約束
public class Product {
    private int stockQuantity;
    private int minStockLevel;
}
@PostMapping("/products")
public ResponseEntity<?> createProduct(
    @Valid @RequestBody Product product // 觸發(fā)驗證
) {
    // ...
}

注意:類級別約束應該加在需要驗證的目標類(實體類/DTO)的類聲明上,且在 Controller 方法參數(shù)添加 @Valid 和在 Service 方法參數(shù)添加 @Valid

3. 異常處理

建議通過 @RestControllerAdvice 設置全局異常處理來對此類異常進行處置。但獲取信息時,這兩類約束的方法不同。
在 MethodArgumentNotValidException 中,字段級約束產(chǎn)生 FieldError,類級約束產(chǎn)生 ObjectError(也稱為全局錯誤)

1) 字段級別約束

獲取方式為 ex.getBindingResult().getFieldErrors(),包含字段名、錯誤消息、拒絕值。
ex.getMessage() 會返回詳細錯誤信息

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
    // 只獲取字段錯誤
    String errorMsg = ex.getBindingResult().getFieldErrors().stream()
            .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
            .collect(Collectors.joining("; "));
    log.warn("請求值驗證異常 | request={}", ex.getMessage());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMsg);
}

2) 類級別約束

獲取方式為 ex.getBindingResult().getGlobalErrors(),包含對象名、錯誤消息。
ex.getMessage() 返回通用消息。

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
    // 只獲取字段錯誤
    String errorMsg = ex.getBindingResult().getGlobalErrors().stream()
            .map(DefaultMessageSourceResolvable::getDefaultMessage)
            .collect(Collectors.joining("; "));
    log.warn("請求值驗證異常 | request={}", ex.getMessage());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMsg);
}

在類級別約束的異常處理時,ex.getBindingResult().getFieldErrors() 會返回空值;同樣的,ex.getBindingResult().getGlobalErrors()也會返回空值
因此,可以統(tǒng)一錯誤處理格式

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
    // 創(chuàng)建錯誤消息列表
    List<String> errorMessages = new ArrayList<>();
    // 處理字段錯誤
    ex.getBindingResult().getFieldErrors().forEach(fieldError -> {
        String msg = String.format("%s: %s",
                fieldError.getField(),
                fieldError.getDefaultMessage());
        errorMessages.add(msg);
    });
    // 處理全局錯誤
    ex.getBindingResult().getGlobalErrors().forEach(globalError -> {
        String msg = String.format("全局錯誤: %s", globalError.getDefaultMessage());
        errorMessages.add(msg);
    });
    // 合并所有錯誤消息
    String errorMsg = String.join("; ", errorMessages);
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMsg);
}

4. 進一步學習

值得注意的是, ex.getMessage() 給出的結果也是不一樣的,這個與代碼實現(xiàn)有關

public String getMessage() {
    StringBuilder sb = new StringBuilder("Validation failed for argument at index ")
            .append(getParameter().getParameterIndex()).append(" in method: ")
            .append(getParameter().getExecutable().toGenericString());
    BindingResult result = getBindingResult();
    if (result.getErrorCount() > 0) {
        sb.append(", with ").append(result.getErrorCount()).append(" error(s): ");
        for (ObjectError error : result.getAllErrors()) {
            sb.append('[').append(error).append("] ");
        }
    }
    return sb.toString();
}

當只有類級約束失敗時,result.getErrorCount() == 1(只有 ObjectError),因此,ex.getMessage() 輸出的內(nèi)容不同

  1. 另外,在調試的過程中還遇到了一個問題,在類級別約束中,考慮到已經(jīng)存在非空校驗了,因此,在類約束中再重復做校驗。但在測試過程中發(fā)現(xiàn),輸入屬性為空時,會在類約束中報錯。后來經(jīng)過查驗,確定問題原因。
    1. 雖然對于 Bean 的校驗按照 字段->類 的順序進行,但校驗過程會收集所有違反約束的情況,而不是在遇到第一個約束失敗時就停止。也就是說類級別的約束(即放在類上的注解)的校驗器,在字段級別約束失敗時,仍然會被執(zhí)行

到此這篇關于SpringBoot 異常處理/自定義格式校驗的文章就介紹到這了,更多相關springboot異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

棋牌| 临武县| 台南县| 基隆市| 藁城市| 石台县| 临颍县| 阿荣旗| 常山县| 尤溪县| 宁河县| 阳西县| 宽甸| 宁明县| 阜南县| 潞城市| 荆州市| 宁波市| 萍乡市| 黎城县| 黄浦区| 清水河县| 南投县| 株洲县| 西畴县| 沾益县| 中西区| 太康县| 疏附县| 交城县| 上思县| 巴中市| 永济市| 五华县| 会东县| 广宗县| 伊宁县| 五寨县| 桃源县| 福安市| 晴隆县|