分組校驗(Group Validation)允許在不同的場景下對同一個實體類應用不同的校驗規則。例如,在新增數據和更新數據時,可能需要對某些字段的校驗規則進行調整。以下是分組校驗的具體實現步驟:
一、定義分組接口
創建空的標記接口(僅用于分組標識):
// 新增時的校驗分組
public interface CreateGroup {}// 更新時的校驗分組
public interface UpdateGroup {}
二、在實體類中指定分組
在字段的校驗注解中,通過 groups
屬性指定所屬分組:
public class User {@NotBlank(message = "用戶ID不能為空", groups = UpdateGroup.class)private String id;@NotBlank(message = "用戶名不能為空", groups = CreateGroup.class)private String username;@Size(min = 6, max = 20, message = "密碼長度需在6到20位之間", groups = {CreateGroup.class, UpdateGroup.class})private String password;// 省略Getter和Setter
}
id
字段:僅在UpdateGroup
分組下校驗(更新時必須校驗)。username
字段:僅在CreateGroup
分組下校驗(新增時必須校驗)。password
字段:在CreateGroup
和UpdateGroup
分組下均校驗(新增和更新時都校驗)。
三、在Controller中指定校驗分組
在Controller方法參數上使用 @Validated
注解(注意是 Spring 的注解,而非 @Valid
),并指定分組:
@RestController
public class UserController {// 新增用戶(校驗 CreateGroup 分組)@PostMapping("/users")public String createUser(@Validated(CreateGroup.class) @RequestBody User user) {return "用戶新增成功";}// 更新用戶(校驗 UpdateGroup 分組)@PutMapping("/users/{id}")public String updateUser(@Validated(UpdateGroup.class) @RequestBody User user) {return "用戶更新成功";}
}
四、全局異常處理
分組校驗失敗會拋出 ConstraintViolationException
,需在全局異常處理器中捕獲:
@RestControllerAdvice
public class GlobalExceptionHandler {@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(ConstraintViolationException.class)public Map<String, Object> handleConstraintViolationException(ConstraintViolationException ex) {Map<String, String> errors = new HashMap<>();ex.getConstraintViolations().forEach(violation -> {String field = violation.getPropertyPath().toString();String message = violation.getMessage();errors.put(field, message);});return Map.of("code", HttpStatus.BAD_REQUEST.value(),"message", "參數校驗失敗","data", errors);}
}
五、測試示例
1. 新增用戶(觸發 CreateGroup
分組校驗)
請求:
POST /users
Content-Type: application/json{"password": "12345"
}
響應:
{"code": 400,"message": "參數校驗失敗","data": {"username": "用戶名不能為空","password": "密碼長度需在6到20位之間"}
}
2. 更新用戶(觸發 UpdateGroup
分組校驗)
請求:
PUT /users/123
Content-Type: application/json{"password": "12345"
}
響應:
{"code": 400,"message": "參數校驗失敗","data": {"id": "用戶ID不能為空","password": "密碼長度需在6到20位之間"}
}
六、分組校驗的高級用法
1. 多分組組合校驗
可以在 @Validated
中同時指定多個分組:
@Validated({CreateGroup.class, AnotherGroup.class})
2. 默認分組
如果字段未指定 groups
屬性,則默認屬于 Default
分組。可以通過 @Validated
的 value
屬性同時包含默認分組:
@Validated(value = {UpdateGroup.class, Default.class})
3. 繼承分組
分組接口可以繼承其他接口,形成層級關系:
public interface AdvancedGroup extends CreateGroup {}
七、分組校驗 vs 多個DTO
方案 | 優點 | 缺點 |
---|---|---|
分組校驗 | 避免創建多個相似DTO,減少冗余代碼 | 實體類可能包含不同場景的注解 |
多個DTO | 職責單一,結構清晰 | 需要維護多個DTO類 |
總結
通過分組校驗,可以靈活控制不同場景下的校驗規則,避免為每個場景創建單獨的DTO類。關鍵步驟:
- 定義分組接口。
- 在實體類字段的校驗注解中指定
groups
。 - 在Controller方法參數上使用
@Validated(分組.class)
。 - 全局捕獲
ConstraintViolationException
并返回自定義錯誤。