Java请求校验流程如何统一

wen java案例 28

本文目录导读:

Java请求校验流程如何统一

  1. 核心思路:三层统一
  2. 完整实现代码
  3. 进阶技巧:分组校验
  4. 自定义校验注解
  5. 最佳实践总结
  6. Maven依赖(Spring Boot已内置)

针对Java请求校验流程的统一,核心在于利用框架的校验注解 + 全局异常处理 + 自定义校验器,形成标准化的处理模式,以下是目前最主流、最推荐的统一实现方案:

核心思路:三层统一

1 注解层:统一校验规则

  • 使用 javax.validationjakarta.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判断,提高了代码的可维护性和可读性。

抱歉,评论功能暂时关闭!