基于Spring Validation实现全局参数校验异常处理的示例详解
作者:小厂永远得不到的男人
在 Spring Boot 项目开发中,接口参数校验是保障数据合法性的关键环节。Spring Validation 提供了便捷的参数校验能力,而全局异常处理则能统一规范校验失败的响应格式,提升接口友好性。本文将完整介绍如何整合两者,实现高效的参数校验与异常处理。
一、核心依赖引入
首先需在pom.xml
(Maven)或build.gradle
(Gradle)中引入 Spring Validation 核心依赖,用于支持参数校验注解。
Maven 依赖
<!-- Spring Validation 核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- Web依赖(若项目已引入可忽略) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
二、参数校验基础使用(注解式校验)
通过在实体类字段或接口方法参数上添加校验注解,实现 “声明式” 参数校验,无需手动编写校验逻辑。
1. 实体类参数校验(常用场景)
在接收请求体的 DTO(数据传输对象)中,为字段添加@NotNull
、@NotBlank
、@Min
等注解,定义校验规则。
import jakarta.validation.constraints.*; // Spring Boot 3.x使用jakarta包,2.x为javax包 public class UserDTO { // 主键非空 @NotNull(message = "用户ID不能为空") private Long id; // 用户名非空且长度1-20 @NotBlank(message = "用户名不能为空") @Size(min = 1, max = 20, message = "用户名长度需在1-20字符之间") private String username; // 年龄18-60岁 @Min(value = 18, message = "年龄不能小于18岁") @Max(value = 60, message = "年龄不能大于60岁") private Integer age; // 邮箱格式合法 @Email(message = "邮箱格式不正确") private String email; // getter/setter省略 }
2. 接口方法添加校验触发注解
在 Controller 接口的参数前添加@Valid
(或@Validated
)注解,触发 Spring Validation 的校验逻辑。
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import jakarta.validation.Valid; @RestController public class UserController { // 新增用户接口,@Valid触发UserDTO的校验 @PostMapping("/user/add") public String addUser(@Valid @RequestBody UserDTO userDTO) { // 校验通过后执行业务逻辑 return "用户新增成功:" + userDTO.getUsername(); } }
三、全局参数校验异常处理(核心)
若参数校验失败,Spring 会抛出MethodArgumentNotValidException
(请求体参数校验失败)或ConstraintViolationException
(路径 / 请求参数校验失败)。通过@RestControllerAdvice
定义全局异常处理器,统一捕获并处理这些异常,返回标准化响应。
1. 定义标准化响应体
先创建统一的接口响应类,确保异常响应与正常响应格式一致。
import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class Result<T> { // 状态码:200成功,400参数错误,500系统错误 private Integer code; // 提示信息 private String message; // 响应数据(成功时返回,失败时为null) private T data; // 静态工厂方法:参数错误响应 public static Result<Void> paramError(String message) { return new Result<>(400, message, null); } // 静态工厂方法:成功响应(可省略) public static <T> Result<T> success(T data) { return new Result<>(200, "success", data); } }
2. 全局异常处理器实现
通过@RestControllerAdvice
注解标识全局异常处理类,用@ExceptionHandler
指定捕获的异常类型,编写处理逻辑。
import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import java.util.stream.Collectors; // 全局异常处理,作用于所有@RestController @RestControllerAdvice public class GlobalValidationExceptionHandler { /** * 处理请求体参数校验失败(@RequestBody + @Valid) * 异常类型:MethodArgumentNotValidException */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result<Void> handleMethodArgumentNotValid(MethodArgumentNotValidException e) { // 提取所有字段的错误信息(如多个参数校验失败,合并提示) String errorMsg = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) // 获取每个字段的自定义错误信息 .collect(Collectors.joining(";")); // 用“;”分隔多个错误 // 返回参数错误响应 return Result.paramError(errorMsg); } /** * 处理路径参数/请求参数校验失败(如@RequestParam/@PathVariable) * 异常类型:ConstraintViolationException */ @ExceptionHandler(ConstraintViolationException.class) public Result<Void> handleConstraintViolation(ConstraintViolationException e) { // 提取参数错误信息 String errorMsg = e.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining(";")); return Result.paramError(errorMsg); } }
四、效果测试与常见场景
1. 请求体参数校验失败示例
向/user/add
接口发送如下请求(用户名空、年龄 17):
{ "id": 1, "username": "", "age": 17, "email": "invalid-email" }
全局异常处理器会返回响应:
{ "code": 400, "message": "用户名不能为空;年龄不能小于18岁;邮箱格式不正确", "data": null }
2. 路径参数校验失败示例
若接口用@PathVariable
接收参数并校验:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import jakarta.validation.constraints.Min; @GetMapping("/user/{userId}") public String getUser(@Min(value = 1, message = "用户ID不能小于1") @PathVariable Long userId) { return "查询用户ID:" + userId; }
访问/user/0
时,会返回:
{ "code": 400, "message": "用户ID不能小于1", "data": null }
五、关键注意事项
- 注解包路径问题:Spring Boot 3.x 版本校验注解从
javax.validation.constraints
迁移到jakarta.validation.constraints
,引入依赖时需注意包名匹配,避免类找不到异常。 - @Valid 与 @Validated 区别:
@Valid
是 JSR-380 标准注解,支持嵌套校验(如 DTO 内部包含另一个需要校验的对象);@Validated
是 Spring 扩展注解,支持分组校验,两者均可触发校验。 - 分组校验扩展:若同一 DTO 在不同接口需不同校验规则(如 “新增用户” 无需 ID,“修改用户” 必须有 ID),可通过 “分组” 实现,需在注解中指定
groups
属性,并在@Validated
中指定分组。
通过以上步骤,可实现 “注解式参数校验 + 全局异常统一处理” 的完整方案,既减少重复校验代码,又保证接口响应格式统一,大幅提升开发效率与接口健壮性。
到此这篇关于基于Spring Validation实现全局参数校验异常处理的示例详解的文章就介绍到这了,更多相关Spring Validation参数校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!