数据校验
Laravel 用
$request->validate([...])或 FormRequest 做校验;Spring Boot 用 Jakarta Bean Validation 标准:规则写在 DTO 字段的注解上,@Valid触发,失败自动抛异常返回 400。
三步使用法
- DTO 字段上加校验注解
- 控制器参数前加
@Valid(或@Validated) - 校验失败抛
MethodArgumentNotValidException(由 异常处理 统一转成响应)
@Data
public class PostDto {
@NotBlank(message = "标题不能为空")
@Size(max = 100, message = "标题最长 100 字")
private String title;
@NotBlank(message = "内容不能为空")
private String content;
@Min(value = 1, message = "状态最小为 1")
private Integer status;
}@PostMapping("/posts")
public Post store(@Valid @RequestBody PostDto dto) {
return service.create(dto);
}TIP
Laravel 的校验错误默认返回 {"title":["标题不能为空"]};Spring 默认返回 400 和一个标准错误体。返回结构如何统一,见文末和 异常处理。
常用校验注解速查
| 注解 | 作用 | 类比 Laravel 规则 |
|---|---|---|
@NotNull | 非 null | required |
@NotBlank | 非空(字符串去空格后非空) | required |
@NotEmpty | 非空(集合/字符串长度>0) | required |
@Size(min,max) | 长度范围 | min:5|max:10 |
@Min / @Max | 数值范围 | min / max |
@Email | 邮箱格式 | email |
@Pattern(regexp) | 正则 | regex:... |
@Positive | 正整数 | integer + min:1 |
@Past / @Future | 时间在前后 | before:... / after:... |
@Valid | 嵌套对象继续校验 | 嵌套规则数组 |
@NotNull vs @NotBlank
@NotNull:值不是 null 就行,空字符串也通过@NotBlank:非 null 且去空格后非空(字符串场景推荐用这个)
分组校验:按场景应用不同规则
一个 DTO 用于「创建」和「更新」时规则不同(更新可空),用分组:
public interface Create {}
public interface Update {}
@Data
public class PostDto {
@NotBlank(groups = Create.class, message = "标题不能为空")
@Size(max = 100, groups = {Create.class, Update.class})
private String title;
}@PostMapping("/posts")
public Post store(@Validated(Create.class) @RequestBody PostDto dto) { ... }
@PutMapping("/posts/{id}")
public Post update(@Validated(Update.class) @RequestBody PostDto dto) { ... }TIP
Laravel 里你通常会写两个 FormRequest(StorePostRequest / UpdatePostRequest)。Spring 用「分组」在一个 DTO 里实现,或直接建两个 DTO。团队可自行选择。
自定义校验规则
场景:校验「标题不能包含敏感词」
写一个注解:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NoBadWordsValidator.class)
public @interface NoBadWords {
String message() default "包含敏感词";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}实现校验器:
public class NoBadWordsValidator implements ConstraintValidator<NoBadWords, String> {
private static final List<String> BAD_WORDS = List.of("广告", "赌博");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.isBlank()) return true;
return BAD_WORDS.stream().noneMatch(value::contains);
}
}使用:
@Data
public class PostDto {
@NoBadWords
private String title;
}类比 Laravel
Laravel 里是 Validator::extend('no_bad_words', ...) 或规则类。Spring 里是「注解 + 校验器」成对出现,注解本身不带逻辑,逻辑全在 ConstraintValidator 里。
控制器方法参数直接校验
路径参数和查询参数也能校验(用 @Validated):
@RestController
@Validated // 类上加,参数校验才生效
public class PostController {
@GetMapping("/posts/{id}")
public Post show(@PathVariable @Min(1) Long id,
@RequestParam @Size(max = 20) String keyword) {
...
}
}失败抛 ConstraintViolationException,同样交给异常处理器。
校验失败的统一响应
默认的 400 响应体比较「框架味」。想统一成 {code, message, errors},在全局异常处理器里处理:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<Void> handleValidation(MethodArgumentNotValidException ex) {
String message = ex.getBindingResult().getFieldErrors().stream()
.map(f -> f.getField() + ": " + f.getDefaultMessage())
.collect(Collectors.joining("; "));
return ApiResponse.of(400, message, null);
}
}完整做法见 异常处理。
校验与 Laravel 对照
| Laravel | Spring Boot |
|---|---|
$request->validate([...]) | DTO 字段注解 + @Valid |
| FormRequest 类 | DTO + 校验注解(或分组) |
'title' => 'required|max:100' | @NotBlank @Size(max=100) |
| 自定义规则 | 自定义注解 + ConstraintValidator |
$validator->errors() | BindingResult / 异常里的字段错误 |
abort(422, $errors) | 抛 MethodArgumentNotValidException |
关于 HTTP 状态码
Laravel 校验失败默认 422;Spring 默认 400。如果团队要 REST 语义,可以在异常处理器里把校验错误映射成 422。别混用。