本文目录导读:

针对Java请求校验流程的统一,核心在于利用框架的校验注解 + 全局异常处理 + 自定义校验器,形成标准化的处理模式,以下是目前最主流、最推荐的统一实现方案:
核心思路:三层统一
1 注解层:统一校验规则
- 使用
javax.validation或jakarta.validation标准注解 - 通过
@Valid或@Validated触发校验
2 处理层:统一异常捕获
- 全局异常处理器
@RestControllerAdvice - 统一处理
MethodArgumentNotValidException等校验异常
3 响应层:统一返回格式
- 自定义统一响应体
Result<T> - 封装错误信息返回
完整实现代码
1 统一响应体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}
2 请求体DTO(使用校验注解)
@Data
public class UserCreateRequest {
@NotBlank(message = "用户名不能为空")
@Length(min = 2, max = 20, message = "用户名长度必须在{min}-{max}之间")
private String username;
@NotNull(message = "年龄不能为空")
@Min(value = 0, message = "年龄不能小于0")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
@Email(message = "邮箱格式不正确")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
}
3 控制器层(触发校验)
@RestController
@RequestMapping("/api/users")
@Validated // 开启类级别校验
public class UserController {
@PostMapping
public Result<User> create(@Valid @RequestBody UserCreateRequest request) {
// 只有校验通过才会执行到这里
User user = userService.create(request);
return Result.success(user);
}
@GetMapping("/{id}")
public Result<User> getById(@PathVariable @Min(1) Long id) {
// 路径参数校验
return Result.success(userService.getById(id));
}
}
4 全局统一异常处理
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理 @RequestBody 参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidationExceptions(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
// 获取第一个错误信息(或拼接所有错误)
String message = bindingResult.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining("; "));
log.error("参数校验失败: {}", message);
return Result.error(400, message);
}
/**
* 处理 @PathVariable 和 @RequestParam 校验异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public Result<Void> handleConstraintViolationException(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
String message = violations.stream()
.map(v -> v.getMessage())
.collect(Collectors.joining("; "));
return Result.error(400, message);
}
/**
* 处理参数类型转换异常
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public Result<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
return Result.error(400, "请求体格式错误");
}
/**
* 处理通用异常
*/
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception ex) {
log.error("系统异常", ex);
return Result.error(500, "服务器内部错误");
}
}
进阶技巧:分组校验
针对不同场景(如新增、修改)使用不同校验规则:
@Data
public class UserRequest {
@NotNull(groups = Update.class, message = "ID不能为空")
private Long id;
@NotBlank(groups = {Create.class, Update.class}, message = "用户名不能为空")
private String username;
@Email(groups = {Create.class}, message = "邮箱格式不正确")
private String email;
// 定义分组接口
public interface Create {}
public interface Update {}
}
// 控制器使用
@PostMapping
public Result<User> create(@Validated(UserRequest.Create.class) @RequestBody UserRequest request) {
// ...
}
@PutMapping
public Result<User> update(@Validated(UserRequest.Update.class) @RequestBody UserRequest request) {
// ...
}
自定义校验注解
当标准注解不够用时,创建自己的校验器:
// 1. 定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidator.class)
public @interface IdCard {
String message() default "身份证号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 2. 实现校验器
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
private static final Pattern ID_CARD_PATTERN =
Pattern.compile("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return true; // 不为空交给 @NotBlank 处理
return ID_CARD_PATTERN.matcher(value).matches();
}
}
// 3. 使用
@Data
public class UserRequest {
@IdCard(groups = Create.class)
private String idCard;
}
最佳实践总结
| 组件 | 推荐方案 | 说明 |
|---|---|---|
| 校验框架 | Hibernate Validator | Spring Boot默认集成 |
| 注解方式 | @Valid + @Validated |
@Validated支持分组 |
| 异常处理 | @RestControllerAdvice |
统一拦截 |
| 响应格式 | 统一Result对象 | 保证API一致性 |
| 复杂校验 | 自定义注解 | 复用性强 |
Maven依赖(Spring Boot已内置)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
通过这种方式,所有请求校验逻辑都遵循注解定义规则 → 框架自动校验 → 全局统一处理的标准流程,大大减少了重复的if-else判断,提高了代码的可维护性和可读性。