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

Spring?Boot參數(shù)校驗的8大坑與生產(chǎn)級避坑完整指南

 更新時間:2025年09月28日 11:08:39   作者:北風(fēng)朝向  
在項目開發(fā)中,時常會碰到前端傳遞過來的請求參數(shù)需要校驗,畢竟永遠(yuǎn)不要相信沒有經(jīng)過自己校驗的數(shù)據(jù),這篇文章主要介紹了Spring?Boot參數(shù)校驗的8大坑與生產(chǎn)級避坑的相關(guān)資料,需要的朋友可以參考下

SpringBoot 參數(shù)校驗?別再讓 @Valid 變成“擺設(shè)”了!

你是不是也遇到過這種情況:

接口加了 @Valid,實體類上寫了 @NotNull、@Size,前端傳了個空字符串,后端日志里卻一臉平靜:“200 OK”,連個警告都沒有?

你查了三遍注解有沒有寫錯,確認(rèn)了 validation-apihibernate-validator 都在依賴?yán)?,甚至重啟了三次服?wù)……
結(jié)果?校驗壓根沒生效。

不是 SpringBoot 懶,也不是你命不好——是你還沒搞懂:@Valid 不是魔法咒語,它是一把需要正確握持的手術(shù)刀。

今天,我就帶你把 SpringBoot 參數(shù)校驗的“潛規(guī)則”扒個底朝天。

不講廢話,不堆配置,只講那些讓你半夜改 Bug 時想砸鍵盤的坑,和真正能讓你代碼穩(wěn)如老狗的正解。

原理淺析:@Valid是怎么“被調(diào)用”的?

很多人以為:只要在 Controller 參數(shù)上加 @Valid,Spring 就會自動幫你校驗。

錯!

它不是“自動”,而是“被動觸發(fā)”——觸發(fā)的條件,比你想象的苛刻得多。

我們來畫個流程圖,看看一次請求從接收到響應(yīng),校驗器到底在哪個環(huán)節(jié)“被喚醒”:

關(guān)鍵點(diǎn)來了

Spring 的校驗機(jī)制,只在參數(shù)解析階段生效,且必須通過 Spring 的參數(shù)解析器(HandlerMethodArgumentResolver)觸發(fā)。
如果你繞過了它——比如在方法內(nèi)部手動 new 一個對象、用 @RequestBody 傳了個 Map、或者在 Service 層直接調(diào)用 Controller 方法——校驗器就徹底“失聯(lián)”了。

更致命的是:校驗失敗不會拋異常!它只會把錯誤塞進(jìn) BindingResult 里。

如果你忘了檢查 BindingResult.hasErrors(),那就等于在高速公路上閉眼開車——系統(tǒng)不報錯,不代表你沒撞墻。

八大坑點(diǎn)代碼實錄

坑1:@Valid加在 Controller 方法參數(shù)上,但沒處理BindingResult

// ? 錯誤示范:校驗了,但沒管結(jié)果
@PostMapping("/user")
public ResponseEntity<String> createUser(@Valid @RequestBody UserDto userDto) {
    // 校驗失敗了?沒關(guān)系,繼續(xù)執(zhí)行!
    userService.save(userDto); // 即使 email 為空,也照存不誤!
    return ResponseEntity.ok("success");
}

你以為加了 @Valid 就萬事大吉?

錯!

Spring 會執(zhí)行校驗,但不會自動拋異常。它把錯誤信息封裝在 BindingResult 里,默認(rèn)行為是忽略。

? 正確做法:顯式檢查 BindingResult

@PostMapping("/user")
public ResponseEntity<?> createUser(@Valid @RequestBody UserDto userDto, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
            .collect(Collectors.toList());
        return ResponseEntity.badRequest().body(errors);
    }
    userService.save(userDto);
    return ResponseEntity.ok("success");
}

?? 最佳實踐:封裝一個全局異常處理器,統(tǒng)一處理 MethodArgumentNotValidException,別在每個接口里寫 if (bindingResult.hasErrors()) —— 后面我會給你模板。

坑2:在 Service 層手動 new 對象,然后傳給@Valid方法

// ? 錯誤示范:校驗失效的“經(jīng)典陷阱”
@Service
public class UserService {

    @Autowired
    private UserController userController; // 別學(xué)這個!這是反模式!

    public void registerUser(String email, String name) {
        UserDto userDto = new UserDto(); // 手動 new!
        userDto.setEmail(email);
        userDto.setName(name);

        // ? 這里調(diào)用 Controller 方法,但 Spring 代理失效!
        userController.createUser(userDto); // @Valid 完全沒生效!
    }
}

你以為你在調(diào)用 @Valid 方法,其實你調(diào)的是原始對象的方法,繞過了 Spring AOP 代理!

Spring 的 @Valid 校驗依賴于 Spring MVC 的參數(shù)解析器,而你直接 new + this.method(),相當(dāng)于跳過了整個 Spring 生命周期。

? 正確做法:校驗邏輯下沉,統(tǒng)一在 Service 層校驗

@Service
public class UserService {

    @Autowired
    private Validator validator; // 注入標(biāo)準(zhǔn) JSR-303 Validator

    public void registerUser(String email, String name) {
        UserDto userDto = new UserDto();
        userDto.setEmail(email);
        userDto.setName(name);

        Set<ConstraintViolation<UserDto>> violations = validator.validate(userDto);
        if (!violations.isEmpty()) {
            throw new ValidationException(violations.stream()
                .map(v -> v.getPropertyPath() + ": " + v.getMessage())
                .collect(Collectors.joining("; ")));
        }

        userRepository.save(userDto);
    }
}

? 你可能會問:Validator 從哪來?
在 Spring Boot 中,它會自動注冊為 Bean。你直接 @Autowired 就行。

坑3:用@Valid校驗Map、List或非 POJO 參數(shù)

// ? 錯誤示范:對 Map 使用 @Valid,以為能校驗內(nèi)容
@PostMapping("/batch")
public ResponseEntity<?> batchCreate(@Valid @RequestBody Map<String, Object> params) {
    // ? 完全無效!@Valid 對 Map 無感!
    String email = (String) params.get("email");
    String name = (String) params.get("name");
    // ... 你得自己寫 if(email == null) ...
    return ResponseEntity.ok("ok");
}

@Valid 只對Java Bean有效。

Map、List、StringInteger……這些都不是 Java Bean,Spring 根本不會遞歸校驗它們內(nèi)部的值

? 正確做法:用封裝類包裝復(fù)雜結(jié)構(gòu)

// ? 正確:定義一個校驗友好的 DTO
public class BatchCreateRequest {
    @Valid
    @NotNull
    @Size(min = 1, max = 100)
    private List<UserDto> users;

    // getter / setter
}

@PostMapping("/batch")
public ResponseEntity<?> batchCreate(@Valid @RequestBody BatchCreateRequest request) {
    // ? 這里會遞歸校驗 List<UserDto> 中每個元素
    request.getUsers().forEach(user -> userService.save(user));
    return ResponseEntity.ok("ok");
}

?? 更進(jìn)一步:如果你要校驗 Map<String, UserDto>,可以定義一個包裝類:

public class UserMapWrapper {
    @Valid
    @NotNull
    private Map<String, UserDto> users;

    // getter/setter
}

Spring 會遞歸校驗 map 的每一個 value!

高階避坑指南:讓校驗系統(tǒng)真正“生產(chǎn)級可用”

1. 全局異常處理器:告別BindingResult的重復(fù)代碼

@RestControllerAdvice
public class GlobalValidationHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
        ErrorResponse error = ErrorResponse.builder()
            .code("VALIDATION_FAILED")
            .message("參數(shù)校驗失敗")
            .details(ex.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(
                    fe -> fe.getField(),
                    fe -> fe.getDefaultMessage()
                )))
            .build();
        return ResponseEntity.badRequest().body(error);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorResponse> handleConstraintViolation(ConstraintViolationException ex) {
        ErrorResponse error = ErrorResponse.builder()
            .code("VALIDATION_FAILED")
            .message("參數(shù)校驗失敗")
            .details(ex.getConstraintViolations().stream()
                .collect(Collectors.toMap(
                    v -> v.getPropertyPath().toString(),
                    v -> v.getMessage()
                )))
            .build();
        return ResponseEntity.badRequest().body(error);
    }
}

這樣,你再也不用在每個接口里寫 if (bindingResult.hasErrors())校驗失敗自動返回 400 + 結(jié)構(gòu)化錯誤,前端直接能用。

2. 自定義校驗注解:別再用@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")了!

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EmailFormatValidator.class)
public @interface ValidEmail {
    String message() default "郵箱格式不正確";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class EmailFormatValidator implements ConstraintValidator<ValidEmail, String> {
    private static final String EMAIL_PATTERN = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";

    @Override
    public boolean isValid(String email, ConstraintValidatorContext context) {
        return email != null && email.matches(EMAIL_PATTERN);
    }
}

然后在 DTO 里:

@ValidEmail
private String email;

可讀性爆炸提升,團(tuán)隊協(xié)作效率翻倍。

3. 校驗分組:同一個 DTO,不同場景,不同規(guī)則

public interface CreateGroup {}
public interface UpdateGroup {}

public class UserDto {
    @NotNull(groups = CreateGroup.class)
    private Long id;

    @NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
    private String name;

    @Email(groups = CreateGroup.class)
    private String email;
}

// Controller 中指定分組
@PostMapping("/create")
public ResponseEntity<?> create(@Validated(CreateGroup.class) @RequestBody UserDto userDto) {
    // 只校驗 CreateGroup 的規(guī)則,id 必須非空,email 必須合法
}

@PostMapping("/update")
public ResponseEntity<?> update(@Validated(UpdateGroup.class) @RequestBody UserDto userDto) {
    // id 可以為 null,但 name 必須存在
}

這才是真正的“生產(chǎn)級校驗”,不是“一招鮮吃遍天”。

總結(jié):校驗不是加個注解就完事了

誤區(qū)真相
@Valid 是魔法它是 Spring MVC 的“觸發(fā)器”,不是“執(zhí)行器”
校驗失敗會拋異常它只會塞進(jìn) BindingResult,你得主動查
@Valid 能校驗 Map/List它只能校驗 Java Bean,嵌套對象要包裝
Service 里調(diào) Controller 方法能校驗你繞過了代理,校驗器根本看不見你
只要加了依賴就生效你得確保校驗器被正確注入、被正確觸發(fā)

真正的高手,從不依賴“自動”。

他們知道:任何“自動”背后,都是有人在默默處理邊界。

你寫的每一行 @Valid,都應(yīng)該有對應(yīng)的 BindingResult、有清晰的異常處理、有可復(fù)用的校驗邏輯。

別再讓校驗變成“看上去很美”的裝飾品了。

讓它成為你系統(tǒng)的第一道防火墻。

下次再有人問你:“為什么我的校驗沒生效?”

你可以微笑著,遞上一杯咖啡,然后說:

“兄弟,你是不是又 new 了一個對象?”

到此這篇關(guān)于Spring Boot參數(shù)校驗的8大坑與生產(chǎn)級避坑完整指南的文章就介紹到這了,更多相關(guān)SpringBoot參數(shù)校驗避坑內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java8接口中引入default關(guān)鍵字的本質(zhì)原因詳析

    Java8接口中引入default關(guān)鍵字的本質(zhì)原因詳析

    Default方法是在java8中引入的關(guān)鍵字,也可稱為Virtual extension methods—虛擬擴(kuò)展方法,這篇文章主要給大家介紹了關(guān)于Java8接口中引入default關(guān)鍵字的本質(zhì)原因,需要的朋友可以參考下
    2022-01-01
  • IDEA連接mysql數(shù)據(jù)庫報錯的解決方法

    IDEA連接mysql數(shù)據(jù)庫報錯的解決方法

    這篇文章主要介紹了IDEA連接mysql數(shù)據(jù)庫報錯的解決方法,文中有非常詳細(xì)的圖文示例,對出現(xiàn)Server returns invalid timezone. Go to ‘Advanced‘ tab and set ‘serverTimezone‘ prope報錯的小伙伴們很有幫助喲,需要的朋友可以參考下
    2021-05-05
  • 詳解JAVA 時間處理相關(guān)類

    詳解JAVA 時間處理相關(guān)類

    這篇文章主要介紹了JAVA 時間處理相關(guān)類的知識,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • IntelliJ IDEA中ajax開發(fā)實現(xiàn)分頁查詢示例

    IntelliJ IDEA中ajax開發(fā)實現(xiàn)分頁查詢示例

    這篇文章主要介紹了IntelliJ IDEA中ajax開發(fā)實現(xiàn)分頁查詢,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • SpringBoot JPA實現(xiàn)增刪改查、分頁、排序、事務(wù)操作等功能示例

    SpringBoot JPA實現(xiàn)增刪改查、分頁、排序、事務(wù)操作等功能示例

    本篇文章主要介紹了SpringBoot JPA實現(xiàn)增刪改查、分頁、排序、事務(wù)操作等功能示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • java 獲取內(nèi)存使用率的流程實例詳解

    java 獲取內(nèi)存使用率的流程實例詳解

    這篇文章主要為大家介紹了java 獲取內(nèi)存使用率的流程實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • springboot layui hutool Excel導(dǎo)入的實現(xiàn)

    springboot layui hutool Excel導(dǎo)入的實現(xiàn)

    本文主要介紹了springboot layui hutool Excel導(dǎo)入的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-03-03
  • Spring context:component-scan的使用及說明

    Spring context:component-scan的使用及說明

    這篇文章主要介紹了Spring context:component-scan的使用及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java調(diào)用瀏覽器打開網(wǎng)頁完整實例

    Java調(diào)用瀏覽器打開網(wǎng)頁完整實例

    這篇文章主要介紹了Java調(diào)用瀏覽器打開網(wǎng)頁的方法,以完整實例形式分析了java打開網(wǎng)頁的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • Java實現(xiàn)MD5消息摘要算法

    Java實現(xiàn)MD5消息摘要算法

    本篇文章主要介紹了Java實現(xiàn)MD5消息摘要算法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04

最新評論

余江县| 赞皇县| 平泉县| 泰安市| 靖安县| 教育| 柳州市| 济源市| 南昌市| 双鸭山市| 上思县| 英吉沙县| 黎城县| 沈丘县| 临潭县| 梁河县| 松溪县| 旬阳县| 孙吴县| 云林县| 中方县| 凤凰县| 宣汉县| 德钦县| 南丹县| 壤塘县| 绥宁县| 雅安市| 天门市| 新郑市| 厦门市| 金华市| 田阳县| 广灵县| 岚皋县| 镇康县| 望城县| 北票市| 察雅县| 马山县| 依安县|