
本文详解如何在 Spring REST Controller 中对 JSON 请求体(如 Search 类)进行精准、前置的字段级校验,确保非法字段名、空集合、缺失必填项等错误能及时捕获并返回清晰的 400 响应,避免因反序列化默认行为导致的静默失败。
本文详解如何在 spring rest controller 中对 json 请求体(如 `search` 类)进行精准、前置的字段级校验,确保非法字段名、空集合、缺失必填项等错误能及时捕获并返回清晰的 400 响应,避免因反序列化默认行为导致的静默失败。
在 Spring Boot 中,仅依赖 @Valid 注解本身并不能自动检测 JSON 中的非法字段名(如将 "select" 错写为 "seletc"),因为 Jackson 默认会忽略未知字段(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES = false)。但真正的校验重点在于:确保反序列化后的对象状态合法——例如 select 列表非空、filters 不为 null、每个 Filter 实例自身也满足其约束条件。这才是生产环境必须保障的核心校验目标。
✅ 正确配置校验注解(关键修正)
首先,移除冗余的 @Valid,改用语义更明确、约束更强的验证注解:
public class Search {
@NotEmpty(message = "select 字段不能为空列表")
private List<String> select = new ArrayList<>();
@NotNull(message = "filters 字段不能为 null")
@NotEmpty(message = "filters 列表不能为空")
private List<Filter> filters = new ArrayList<>(); // 注意:此处无需 @Valid —— 若 Filter 类含校验规则,@Valid 将在下一层生效
// 构造函数、getter/setter 省略
}? 说明:
@NotEmpty同时校验null和空集合(size() == 0),比@NotNull更严格;List<filter></filter>本身不需@Valid,但若Filter类中定义了@NotBlank、@Min等注解,则@Valid必须加在filters字段上(见下文);@Valid的作用是递归触发嵌套对象的校验,而非校验集合类型本身。
✅ Controller 层启用全局校验
确保控制器方法参数使用 @Valid 触发校验链,并配合 BindingResult 或全局异常处理器统一响应:
@RestController
public class SearchController {
@PostMapping("/search")
public ResponseEntity<?> results(
@NotNull @PathVariable String id,
@Valid @RequestBody Search search) { // ✅ @Valid + @RequestBody 是标准组合
// 业务逻辑
return ResponseEntity.ok("success");
}
}⚠️ 注意:
@RequestBody是必需的,它明确告知 Spring 该参数来自 JSON 请求体;@Valid则启动 JSR-303 校验流程。
✅ 为嵌套对象(如 Filter)添加校验
若 Filter 类需校验其字段(如 field 非空、value 长度合规),则需在 Search.filters 上添加 @Valid,并在 Filter 中声明约束:
public class Filter {
@NotBlank(message = "filter.field 不能为空")
private String field;
@Size(max = 100, message = "filter.value 长度不能超过 100")
private String value;
// 构造函数、getter/setter
}
// 对应地,Search 中:
@Valid // ✅ 启用对每个 Filter 实例的校验
@NotNull @NotEmpty
private List<Filter> filters = new ArrayList<>();✅ 全局异常处理(推荐实践)
为提升用户体验,建议统一捕获校验异常并返回结构化错误响应:
@ControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
}调用示例(非法请求):
POST /search?id=123
{
"select": [],
"filters": [{}]
}→ 返回 400 Bad Request:
{
"select": "select 字段不能为空列表",
"filters[0].field": "filter.field 不能为空"
}? 关于“检测未知字段”的补充说明
如需严格拒绝含未知字段的 JSON(如 "seletc"),需显式开启 Jackson 配置:
# application.yml
spring:
jackson:
deserialization:
fail-on-unknown-properties: true但请注意:此配置会使整个应用所有 JSON 反序列化均失败于未知字段,可能影响兼容性。更推荐的做法是依靠严谨的 DTO 设计 + 充分的字段级校验,而非依赖未知字段拦截——后者属于防御性设计,前者才是契约驱动的正向保障。
总结:校验的核心在于明确约束语义(@NotEmpty > @NotNull)、合理使用 @Valid 触发嵌套校验、配合 @RequestBody 和全局异常处理器。这样即可在 Spring 生态中构建健壮、可维护、用户友好的 REST 参数校验体系。


















