Skip to content

Spring Boot 参数校验的最佳实践

参数校验是后端开发中最基础但也最容易忽视的部分。本文总结几种常用的校验方式及其适用场景。

🔧 基础校验:使用 Validation 注解

最简单的方式是使用 javax.validation 注解:

java
@Data
public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度 2-20 字符")
    private String username;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Min(value = 1, message = "年龄必须大于 0")
    private Integer age;
}

Controller 中配合 @Valid 使用:

java
@PostMapping("/users")
public Result createUser(@Valid @RequestBody UserDTO userDTO) {
    // 业务逻辑
}

常用注解速查

注解说明适用类型
@NotBlank不能为空且长度 > 0String
@NotNull不能为 null所有类型
@Size长度范围String/Collection
@Min/@Max数值范围Number
@Email邮箱格式String
@Pattern正则匹配String

🎯 进阶:自定义校验注解

当内置注解无法满足需求时,可以自定义:

java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<?>[] payload() default {};
}

public class PhoneValidator implements ConstraintValidator<Phone, String> {
    private static final Pattern PHONE_PATTERN =
        Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) return true; // 允许空值,配合 @NotBlank
        return PHONE_PATTERN.matcher(value).matches();
    }
}

🔄 分组校验:同一实体不同场景

创建和更新往往校验规则不同:

java
public interface Create {}
public interface Update {}

@Data
public class UserDTO {
    @NotNull(groups = Update.class, message = "更新时 ID 必填")
    private Long id;

    @NotBlank(groups = {Create.class, Update.class})
    private String username;
}

// Controller
@PostMapping
public Result create(@Validated(Create.class) @RequestBody UserDTO dto) {}

@PutMapping
public Result update(@Validated(Update.class) @RequestBody UserDTO dto) {}

📝 统一异常处理

校验失败会抛出 MethodArgumentNotValidException,建议统一处理:

java
@RestControllerAdvice
public class ValidationExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidation(MethodArgumentNotValidException e) {
        List<String> errors = e.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(error -> error.getField() + ": " + error.getDefaultMessage())
            .collect(Collectors.toList());

        return Result.fail("参数校验失败", errors);
    }
}

💡 最佳实践建议

  1. DTO 与 Entity 分离:校验注解放在 DTO,Entity 只做持久化
  2. 宽松入参,严格出参:入参允许部分字段为空,出参返回完整数据
  3. 业务校验与格式校验分离:格式用注解,业务逻辑在 Service
  4. 友好错误提示:错误信息要用户能看懂,避免技术术语

📚 参考资源


参数校验看似简单,用好却能减少大量 if-else,让代码更清晰优雅。希望这篇对你有帮助!☕

🫐 BlueberryLab — 技术与生活记录