参数校验与异常处理
约 1002 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › Spring Boot › Web 与 REST › 参数校验与异常处理
本节使用 Jakarta Bean Validation 为输入参数添加校验规则,并通过 @RestControllerAdvice 实现全局异常处理。
Jakarta Bean Validation
Spring Boot 3.x 使用 jakarta.validation 命名空间。引入 spring-boot-starter-web 时已包含校验依赖,无需额外配置。
常见校验注解
| 注解 | 作用 |
|---|---|
@NotNull | 值不能为 null |
@NotBlank | 字符串不能为 null 且不能全为空格 |
@NotEmpty | 集合/数组/字符串不能为 null 且 size > 0 |
@Size(min, max) | 字符串/集合长度范围 |
@Min / @Max | 数字最小值/最大值 |
@Email | 邮箱格式 |
@Pattern(regexp) | 正则匹配 |
在 DTO 上声明校验
public class UserCreateRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度需在 2-20 之间")
private String username;
@NotNull(message = "年龄不能为空")
@Min(value = 0, message = "年龄不能为负")
@Max(value = 150, message = "年龄不能超过 150")
private Integer age;
@Email(message = "邮箱格式不正确")
private String email;
// getter / setter
}在 Controller 中启用校验
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public Result<User> create(@Valid @RequestBody UserCreateRequest request) {
User user = userService.create(request);
return Result.success(user);
}
@PutMapping("/{id}")
public Result<User> update(
@PathVariable Long id,
@Valid @RequestBody UserUpdateRequest request) {
User user = userService.update(id, request);
return Result.success(user);
}
}@Valid 告诉 Spring 在参数绑定后执行校验,如果失败抛出 MethodArgumentNotValidException。
生产意识:
@Valid对嵌套对象的校验是递归的——如果UserCreateRequest中包含一个@Valid Address address字段,Address内的校验注解也会被检查。但集合元素的校验是个常见盲区:List<@Valid @Email String> emails这种写法需要
全局异常处理
如果不处理,校验失败默认返回 400 状态码和 Spring 默认的错误 JSON,格式不是前端期望的统一结构。通过 @RestControllerAdvice 可以统一拦截异常并返回 Result:
@RestControllerAdvice
public class GlobalExceptionHandler {
// 参数校验失败
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidation(MethodArgumentNotValidException ex) {
String message = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.collect(Collectors.joining("; "));
return Result.error(400, message);
}
// 资源不存在
@ExceptionHandler(NoSuchElementException.class)
public Result<Void> handleNotFound(NoSuchElementException ex) {
return Result.error(404, ex.getMessage());
}
// 通用兜底
@ExceptionHandler(Exception.class)
public Result<Void> handleUnknown(Exception ex) {
log.error("未预期异常", ex);
return Result.error(500, "服务器内部错误");
}
}当校验失败时,前端收到的响应:
{
"code": 400,
"message": "username: 用户名长度需在 2-20 之间; email: 邮箱格式不正确"
}异常处理优先级
@ExceptionHandler 会匹配最具体的异常子类。例如 MethodArgumentNotValidException 优先于兜底的 Exception。如果同时存在多个匹配,Spring 使用继承树上最接近的处理器。
自定义业务异常
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() { return code; }
}在处理器中添加:
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusiness(BusinessException ex) {
return Result.error(ex.getCode(), ex.getMessage());
}生产意识:全局异常处理器中务必记录日志(
log.error或log.warn),尤其是兜底的Exception。常见线上问题是兜底处理器只返回了"500 服务器内部错误"却没有日志,导致定位问题需要重新复现。建议至少对BusinessException以下的业务异常用log.warn,对Exception兜底用log.error。
小结
- 校验注解(
@NotBlank、@Size、@Email等)在 DTO 字段上声明规则。 - Controller 参数前加
@Valid启用校验。 @RestControllerAdvice+@ExceptionHandler实现全局异常捕获,返回统一响应。- 优先匹配最具体的异常子类,兜底用
Exception。 - 易错:校验失败时错误信息默认是英文,可通过
ValidationMessages.properties国际化;@Valid只能触发简单的字段校验,跨字段关联校验(如密码确认)需要自定义校验器。切勿在异常处理器中泄漏堆栈详情(如ex.printStackTrace())给前端。 - 思考任务:在项目中定义
BusinessException(如 404 资源未找到、403 无权限),用@RestControllerAdvice统一处理,并在Result中携带错误码。
上一节:REST API 开发
下一节:测试
