由于 @RequestParam 这种直接写在方法参数上的校验,走的是 方法级别校验(Method Validation) 机制,和 @RequestBody 的校验机制完全不同。
以下是这三个关键内容的正确写法和内在逻辑:
1. spring-boot-starter-validation(地基)
-
作用:引入 Hibernate Validator 实现库。没有它,所有的
@NotNull、@Validated和你的自定义注解全部是哑火的废纸。 -
注意:如果是 Spring Boot 2.3 及以上版本,默认不再包含它,必须像你这样手动引入,这一步你做对了。
2. @Validated(触发器)
-
作用:它是 Spring 提供的注解,作用是开启方法参数级别的 AOP 校验。
-
位置:必须加在 Controller 类上,绝对不能加在方法上。
-
避坑:在
@RequestParam参数校验中,千万不要用@Valid!@Valid主要用于级联校验(比如@RequestBody里的实体对象),在这个场景下加@Valid是无效的,甚至可能干扰框架的判断。
3. 自定义注解与校验器(执行者)
这是最容易写错的地方,必须严格遵守 JSR-380 规范(包含特定的三个方法)。
第一步:定义 @ValidFile 注解
java
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.FIELD}) // 必须允许在参数上使用
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FileValidator.class) // 🌟 核心:绑定校验逻辑类
@Documented
public @interface ValidFile {
// 🌟 以下是必须包含的三个标准方法
String message() default "文件不符合要求";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 可选:如果你想把限制值做成可配置的(比如不同接口允许不同大小)
long maxSize() default 50 * 1024 * 1024L; // 默认 50MB
String[] allowTypes() default {"jpg", "jpeg", "png", "pdf", "doc", "docx", "xls", "xlsx"};
}
第二步:实现 FileValidator 校验器
java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {
private long maxSize;
private String[] allowTypes;
// 初始化时,获取注解上配置的参数
@Override
public void initialize(ValidFile constraintAnnotation) {
this.maxSize = constraintAnnotation.maxSize();
this.allowTypes = constraintAnnotation.allowTypes();
}
@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
System.out.println("====== 进入 FileValidator 校验了 ======");
// 1. 如果文件为空,直接返回 true(如果要求不能为空,由 @NotNull 拦截)
if (file == null || file.isEmpty()) {
return true;
}
// 2. 校验大小
if (file.getSize() > maxSize) {
setMessage(context, "文件大小不能超过 " + (maxSize / 1024 / 1024) + "MB");
return false;
}
// 3. 校验后缀名
String fileName = file.getOriginalFilename();
if (fileName == null || !fileName.contains(".")) {
setMessage(context, "文件名不合法");
return false;
}
String ext = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
if (!Arrays.asList(allowTypes).contains(ext)) {
setMessage(context, "不支持的文件类型,仅支持:" + Arrays.toString(allowTypes));
return false;
}
return true;
}
// 自定义返回的错误信息
private void setMessage(ConstraintValidatorContext context, String message) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
}
}
4. 最终 Controller 的正确姿势
以上都准备好后,你的 Controller 必须是这样的:
java
@RestController
@RequestMapping("/doc")
@Validated // 🌟 核心:必须加在类上,开启 AOP 拦截
public class DocInfoController {
@ApiOperation(value = "上传文件")
@PostMapping(value = "/upload")
public CommonResult<FileDTO.UploadRes> uploadFile(
// 🌟 核心:直接使用 @ValidFile,前面不需要加 @Valid
@ValidFile(message = "文件校验失败") @RequestParam(value = "file", required = false) MultipartFile file) {
// 如果校验失败,根本不会执行到这里
return CommonResult.success(docInfoService.uploadFile(file));
}
}
本文详解在Spring Boot中实现方法级参数校验的自定义注解全流程,涵盖Hibernate Validator依赖引入(地基)、@Validated注解启用方法校验(触发器)、符合JSR-380规范的自定义注解定义及ConstraintValidator实现(执行者),并强调Controller类上声明@Validated的关键约束。适用于Spring Boot 2.3+版本的校验机制适配。

7125

被折叠的 条评论
为什么被折叠?



