SpringBoot优雅参数检查
在 Spring Boot 中,参数验证通常基于 JSR-380(Bean Validation 2.0)规范,结合 javax.validation
(或 jakarta.validation
)和 Hibernate Validator 实现。以下是常用的验证注解及其意义,分为 触发验证的注解 和 约束注解 两类:
注解
一、触发验证的注解
-
@Valid
• 作用:触发对对象属性的级联验证(如验证嵌套对象)。• 示例:
@PostMapping("/user") public void createUser(@RequestBody @Valid User user) { ... }
-
@Validated
• 作用:Spring 提供的注解,支持分组验证(Group Validation),用于类或方法级别。• 示例:
@Service @Validated public class UserService { public void updateUser(@Valid User user) { ... } }
二、常用约束注解(Bean Validation)
基本约束
注解 | 作用 | 适用类型 |
---|---|---|
@NotNull | 验证字段值不为 null 。 | 任意类型 |
@Null | 验证字段值为 null 。 | 任意类型 |
@NotEmpty | 验证字符串、集合、数组等不为空(长度/大小 > 0)。 | String, Collection, Map, Array |
@NotBlank | 验证字符串不为空且至少包含一个非空白字符(自动 trim 后检查)。 | String |
@Size | 验证字段大小在指定范围内(min 和 max )。 | String, Collection, Map, Array |
@Min | 验证数值 >= 指定值。 | 数值类型(int, long 等) |
@Max | 验证数值 <= 指定值。 | 同上 |
@DecimalMin | 验证数值 >= 指定值(支持字符串形式的数值,如 "0.1" )。 | 数值类型、String |
@DecimalMax | 验证数值 <= 指定值(同上)。 | 同上 |
@Positive | 验证数值 > 0。 | 数值类型 |
@PositiveOrZero | 验证数值 >= 0。 | 数值类型 |
@Negative | 验证数值 < 0。 | 数值类型 |
@NegativeOrZero | 验证数值 <= 0。 | 数值类型 |
字符串约束
注解 | 作用 | 示例 |
---|---|---|
@Pattern | 验证字符串是否符合正则表达式。 | @Pattern(regexp = "^[a-zA-Z0-9]+$") |
@Email | 验证字符串是否为合法邮箱格式(默认宽松,可通过 regexp 自定义)。 | @Email(message = "邮箱格式错误") |
日期约束
注解 | 作用 | 适用类型 |
---|---|---|
@Past | 验证日期是否在当前时间之前。 | Date, LocalDate 等 |
@PastOrPresent | 验证日期是否在当前时间或之前。 | 同上 |
@Future | 验证日期是否在当前时间之后。 | 同上 |
@FutureOrPresent | 验证日期是否在当前时间或之后。 | 同上 |
其他约束
注解 | 作用 | 适用类型 |
---|---|---|
@AssertTrue | 验证布尔字段为 true 。 | boolean |
@AssertFalse | 验证布尔字段为 false 。 | boolean |
@Digits | 验证数值的整数和小数部分的位数是否符合要求。 | 数值类型 |
@Range | 验证数值是否在指定范围内(Hibernate Validator 扩展)。 | 数值类型 |
三、Hibernate Validator 扩展注解
Hibernate 提供了额外约束注解(需引入 hibernate-validator
依赖):
注解 | 作用 | 适用类型 |
---|---|---|
@Length | 验证字符串长度在范围内(同 @Size ,但仅用于字符串)。 | String |
@URL | 验证字符串是否为合法 URL。 | String |
@CreditCardNumber | 验证字符串是否为合法信用卡号(Luhn 算法)。 | String |
@UniqueElements | 验证集合中的元素是否唯一。 | Collection, Array |
四、自定义验证注解
可通过组合 @Constraint
和自定义验证逻辑实现:
@Target({FIELD, METHOD})
@Retention(RUNTIME)
@Constraint(validatedBy = MyValidator.class)
public @interface MyConstraint {
String message() default "自定义错误消息";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
五、使用示例
public class User {
@NotBlank(message = "用户名不能为空")
private String username;
@Email(message = "邮箱格式错误")
private String email;
@Size(min = 6, max = 20, message = "密码长度需在6-20之间")
private String password;
@Min(value = 18, message = "年龄必须大于18岁")
private Integer age;
@NotNull
@Valid // 嵌套验证
private Address address;
}
通过合理组合这些注解,可以高效地实现参数合法性校验。如果需要分组验证,可结合 @Validated
和 groups
属性。
拦截错误
在 Spring Boot 中,参数校验失败时,框架会抛出 MethodArgumentNotValidException
(针对 @RequestBody
或 @RequestParam
校验失败)或 ConstraintViolationException
(针对方法参数校验失败)。你可以通过 全局异常处理 来统一捕获这些异常,并返回自定义的错误信息。
一、创建全局异常处理类
使用 @RestControllerAdvice
或 @ControllerAdvice
注解定义一个全局异常处理类:
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理请求体校验失败异常(@RequestBody + @Valid 触发)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultVo handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
// 提取校验失败的字段信息
BindingResult bindingResult = ex.getBindingResult();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
// 构造错误信息
List<String> errors = fieldErrors.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
return ResultVo.fail(400, "参数校验失败", errors);
}
// 处理请求参数校验失败异常(@RequestParam/@PathVariable + @Validated 触发)
@ExceptionHandler(ConstraintViolationException.class)
public ResultVo handleConstraintViolationException(ConstraintViolationException ex) {
// 提取校验失败的参数信息
List<String> errors = ex.getConstraintViolations().stream()
.map(violation -> {
String path = violation.getPropertyPath().toString();
return path.substring(path.lastIndexOf('.') + 1) + ": " + violation.getMessage();
})
.collect(Collectors.toList());
return ResultVo.fail(400, "参数校验失败", errors);
}
}
二、定义统一响应格式
创建一个通用的响应类(如 ResultVo
),用于封装错误信息:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVo<T> {
private int code; // 状态码(如 400 表示参数错误)
private String message; // 错误描述
private T data; // 错误详情(如具体字段的校验失败信息)
// 快速生成失败响应
public static <T> ResultVo<T> fail(int code, String message, T data) {
return new ResultVo<>(code, message, data);
}
}
三、错误响应示例
当校验失败时,返回的 JSON 格式如下:
{
"code": 400,
"message": "参数校验失败",
"data": [
"username: 用户名不能为空",
"email: 邮箱格式错误"
]
}
四、其他注意事项
-
自定义错误消息
在@NotBlank
、@Email
等注解中,通过message
属性指定错误提示:@NotBlank(message = "用户名不能为空") private String username;
-
处理其他异常
可以扩展GlobalExceptionHandler
,添加对其他异常(如业务异常、权限异常)的处理。 -
分组校验
如果使用@Validated
分组校验,需确保异常处理逻辑能正确提取分组后的错误信息。
通过这种方式,所有参数校验失败的错误会被统一拦截,并以结构化的 JSON 格式返回给客户端,方便前端处理错误信息。