参数校验

接口入参必须可信:空值、超长、非法格式要在入口处拦截。Spring Boot 整合 Bean Validation,用声明式注解完成校验,无需手写一堆 if。

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

该 starter 自带 jakarta.validation(Bean Validation)API 与 Hibernate Validator 实现。注意:Boot 3 起包名全部是 jakarta.validation.*javax.validation.* 只存在于 2.x)。

请求体校验

定义请求对象(record 更简洁,普通类亦可):

public record CreateUserRequest(
        @NotBlank @Size(max = 20) String name,
        @Min(1) @Max(150) int age,
        @Email String email
) {}

控制器加 @Valid,校验失败时 Spring MVC 抛 MethodArgumentNotValidException,默认响应 400:

@PostMapping("/users")
public User create(@Valid @RequestBody CreateUserRequest req) {
    return userService.create(req);
}

常用约束注解

注解作用适用
@NotNull不能为 null任意对象
@NotEmpty非 null 且非空字符串/集合
@NotBlank非空白串字符串
@Size(min, max)长度/个数范围字符串/集合/数组
@Min / @Max数值上下限数值
@Pattern(regexp)正则匹配字符串
@Email邮箱格式字符串

以上均在 jakarta.validation.constraints 包下。

路径与查询参数校验

控制器类上加 @Validated,参数上直接放约束,失败抛 ConstraintViolationException

@Validated
@RestController
public class UserController {

    @GetMapping("/users/{id}")
    public User get(@PathVariable @Min(1) long id,
                    @RequestParam(defaultValue = "0") @Min(0) int page) {
        return userService.findById(id);
    }
}

分组校验

同一对象在不同接口规则不同(新建必填 name、更新可缺省),用「分组接口」区分:

public interface Create {}
public interface Update {}

public class SaveUserRequest {
    @NotBlank(groups = Create.class)
    private String name;

    @Email(groups = {Create.class, Update.class})
    private String email;
}
@PostMapping("/users")
public void create(@Validated(Create.class) @RequestBody SaveUserRequest req) { }

@PutMapping("/users/{id}")
public void update(@PathVariable long id,
                   @Validated(Update.class) @RequestBody SaveUserRequest req) { }

未写 groups 的约束属于默认 Default 组,校验指定分组时默认组约束不会执行;想让默认组与自定义组一起生效,可让分组接口 extends Default,细节以官方文档为准。

统一异常处理

@RestControllerAdvice 把校验错误转成统一结构:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> handleBody(MethodArgumentNotValidException ex) {
        String msg = ex.getBindingResult().getFieldErrors().stream()
                .map(e -> e.getField() + ": " + e.getDefaultMessage())
                .collect(Collectors.joining("; "));
        return Map.of("code", 400, "message", msg);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Map<String, Object> handleParam(ConstraintViolationException ex) {
        return Map.of("code", 400, "message", ex.getMessage());
    }
}

小结

总结三步:DTO 上声明式加约束 → 入口用 @Valid/@Validated → 全局异常处理统一返回格式。嵌套校验记得给对象字段加 @Valid。分组校验解决「一个对象多种规则」,细节以官方文档为准。

笔记加载中…