@Valid 和 @Validated 比较,以及 SpringBoot 中对 Query、Path、Body 类型参数的校验

在 Spring Boot 中,@Valid@Validated 都用于参数校验,但它们的能力和适用场景不同。

一、@Valid 与 @Validated 对比

对比项 @Valid @Validated
来源 JSR-303/JSR-380 Spring
包路径 jakarta.validation.Valid org.springframework.validation.annotation.Validated
Bean嵌套校验 ✅ 支持 ✅ 支持
分组校验(Group) ❌ 不支持 ✅ 支持
方法级参数校验 ❌ 单独使用无效 ✅ 支持
Spring推荐 一般用于DTO字段 Controller/Service层推荐

@Valid

标准 Bean Validation 注解:

import jakarta.validation.Valid;

例如:

@Data
public class UserDTO {

    @NotBlank
    private String username;

    @Min(18)
    private Integer age;
}

Controller:

@PostMapping("/user")
public String save(@RequestBody @Valid UserDTO dto) {
    return "ok";
}

@Validated

Spring 扩展版:

import org.springframework.validation.annotation.Validated;

支持:

  • Bean校验
  • 分组校验
  • 方法参数校验
@PostMapping("/user")
public String save(@RequestBody @Validated UserDTO dto) {
    return "ok";
}

通常项目里统一使用:

@Validated
@RestController

二、分组校验

例如新增和修改规则不同。

定义分组

public interface AddGroup {}

public interface UpdateGroup {}

DTO

@Data
public class UserDTO {

    @Null(groups = AddGroup.class)
    @NotNull(groups = UpdateGroup.class)
    private Long id;

    @NotBlank(groups = {AddGroup.class, UpdateGroup.class})
    private String username;
}

Controller

新增:

@PostMapping
public void add(
        @RequestBody
        @Validated(AddGroup.class)
        UserDTO dto) {

}

修改:

@PutMapping
public void update(
        @RequestBody
        @Validated(UpdateGroup.class)
        UserDTO dto) {

}

三、Query 参数校验

例如:

GET /user?page=1&size=20

方式1:直接校验单个参数

Controller 必须加:

@Validated
@RestController
public class UserController {

}

否则不会生效。

@GetMapping("/user")
public List<UserVO> list(
        @RequestParam
        @Min(1)
        Integer page,

        @RequestParam
        @Max(100)
        Integer size) {

    return List.of();
}

方式2:封装Query对象

推荐。

@Data
public class UserQuery {

    @Min(1)
    private Integer page;

    @Max(100)
    private Integer size;
}

Controller:

@GetMapping("/user")
public List<UserVO> list(
        @Validated UserQuery query) {

    return List.of();
}

Spring 会自动从 QueryString 绑定。


四、Path 参数校验

例如:

GET /user/100

Controller

类上必须有:

@Validated
@RestController

参数:

@GetMapping("/{id}")
public UserVO detail(
        @PathVariable
        @Min(1)
        Long id) {

    return null;
}

多个约束:

@GetMapping("/{id}")
public UserVO detail(
        @PathVariable
        @NotNull
        @Min(1)
        Long id) {

    return null;
}

五、Body 参数校验

最常见。

DTO:

@Data
public class UserCreateRequest {

    @NotBlank
    private String username;

    @Email
    private String email;

    @Min(18)
    private Integer age;
}

Controller:

@PostMapping
public void create(
        @RequestBody
        @Valid
        UserCreateRequest request) {

}

或者:

@PostMapping
public void create(
        @RequestBody
        @Validated
        UserCreateRequest request) {

}

效果一样。


六、嵌套对象校验

很多人容易漏掉。

DTO:

@Data
public class AddressDTO {

    @NotBlank
    private String city;
}
@Data
public class UserDTO {

    @NotBlank
    private String username;

    @Valid
    private AddressDTO address;
}

必须加:

@Valid
private AddressDTO address;

否则:

address.city

不会校验。


七、List 集合校验

校验 List 本身

@PostMapping
public void save(
        @RequestBody
        @Size(min = 1, max = 100)
        List<UserDTO> users) {

}

校验 List 中元素

@PostMapping
public void save(
        @RequestBody
        List<@Valid UserDTO> users) {

}

或者:

@PostMapping
public void save(
        @RequestBody
        @Valid
        List<UserDTO> users) {

}

Spring Boot 3 推荐:

List<@Valid UserDTO>

八、统一异常处理

Query、Path、Body 的异常类型并不一样。

@RestControllerAdvice
public class GlobalExceptionHandler {
}

Body 校验失败

@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handle(
        MethodArgumentNotValidException e) {

    String msg = e.getBindingResult()
            .getFieldError()
            .getDefaultMessage();

    return Result.fail(msg);
}

对应:

@RequestBody
@Valid

Query / Path 校验失败

@ExceptionHandler(ConstraintViolationException.class)
public Result<Void> handle(
        ConstraintViolationException e) {

    return Result.fail(e.getMessage());
}

对应:

@RequestParam
@PathVariable

参数绑定失败

例如:

/user/abc

而:

@PathVariable Long id

会抛:

MethodArgumentTypeMismatchException

处理:

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public Result<Void> handle(
        MethodArgumentTypeMismatchException e) {

    return Result.fail("参数格式错误");
}

九、企业项目推荐写法

Query

@GetMapping
public Result<PageVO<UserVO>> page(
        @Validated UserQuery query)

Path

@GetMapping("/{id}")
public Result<UserVO> detail(
        @PathVariable
        @Min(1)
        Long id)

Controller 类:

@Validated
@RestController

Body

@PostMapping
public Result<Void> create(
        @RequestBody
        @Validated(CreateGroup.class)
        UserCreateRequest request)

DTO

@Data
public class UserCreateRequest {

    @NotBlank(message = "用户名不能为空")
    private String username;

    @Email(message = "邮箱格式错误")
    private String email;
}

全局异常

统一处理:

  • MethodArgumentNotValidException(Body)
  • ConstraintViolationException(Query/Path)
  • MethodArgumentTypeMismatchException(类型转换)

这是目前 Spring Boot 3.x 项目里最常见、最完整的一套参数校验方案。

posted @ 2026-06-15 10:05  码上布道  阅读(16)  评论(0)    收藏  举报