在芋道(yudao)框架的 APP 接口开发中,我们经常会遇到这样的代码:
java
@GetMapping("/list")
@Operation(summary = "获得活动商品列表(按开始日期分组)")
@PermitAll
public CommonResult<List<AppProductActivityListRespVO>> getActivitySpuList(@Valid AppProductActivityListReqVO reqVO) {
return success(appProductActivityService.getActivitySpuList(reqVO));
}
你可能会好奇 👇
✅ 为什么加上 @Valid
注解就能自动校验参数?
✅ 错误提示是从哪里来的?
✅ 怎么自定义规则,比如"结束时间不能早于开始时间"?
这篇文章就带你从入门到精通,彻底搞懂芋道的参数校验体系。
🧩 一、基础概念:@Valid 是做什么的?
在 Spring Boot 项目中,@Valid
或 @Validated
用于触发参数自动校验机制。
只要在 Controller 的入参上加上它:
java
public CommonResult<?> test(@Valid XxxReqVO reqVO)
Spring 会在调用方法前自动验证 reqVO
里的字段,
如果不符合规则,直接抛出异常(由全局异常处理器拦截返回友好提示)。
🧱 二、核心校验注解讲解
我们看下实际的例子:
java
@Schema(description = "用户 APP - 活动商品查询请求")
@Data
public class AppProductActivityListReqVO {
@Schema(description = "开始日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2025-01-01")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@NotNull(message = "开始日期不能为空")
private LocalDate startTime;
@Schema(description = "结束日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2025-01-31")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@NotNull(message = "结束日期不能为空")
private LocalDate endTime;
@Schema(description = "商品分类编号,可选")
private Long categoryId;
🔹 每个注解的含义如下:
注解 | 作用 | 示例 |
---|---|---|
@Schema |
仅用于 Swagger/OpenAPI 文档描述 | 自动生成接口文档字段说明 |
@DateTimeFormat |
指定前端传入日期的格式 | 避免格式错误,如 "2025-01-01" |
@NotNull |
校验字段不为空 | "开始日期不能为空" |
@Valid |
触发整个对象的校验 | 用在 Controller 入参上 |
@Data |
Lombok 注解,自动生成 getter/setter | 简化代码 |
🧮 三、进阶校验:自定义逻辑(@AssertTrue)
除了基本的非空、长度、正则验证外,芋道还经常使用自定义逻辑校验。
看下面两个方法 👇
java
@AssertTrue(message = "结束时间不能早于开始时间")
@JsonIgnore
public boolean isEndNotBeforeStart() {
if (startTime == null || endTime == null) {
return true; // 交给 @NotNull 提示
}
return !endTime.isBefore(startTime);
}
@AssertTrue(message = "时间范围不能超过90天")
@JsonIgnore
public boolean isRangeWithinLimit() {
if (startTime == null || endTime == null) {
return true;
}
return ChronoUnit.DAYS.between(startTime, endTime) <= 90;
}
✨ 含义解析:
注解/方法 | 含义 |
---|---|
@AssertTrue |
表示该方法返回 true 时校验通过;返回 false 时提示错误信息 |
@JsonIgnore |
防止这个方法被序列化到 JSON 中(否则会在响应中看到它) |
方法名通常以 is...() 开头 |
Java Bean Validation 默认识别布尔校验方法 |
这样写的好处:
- 不用写自定义 Validator;
- 校验逻辑与字段关系紧密;
- 一目了然,错误提示友好。
🧰 四、Spring 如何处理这些错误?
当参数不符合要求时(例如 endTime
早于 startTime
),
Spring 会抛出 MethodArgumentNotValidException
,
芋道框架的全局异常处理器会捕获它,返回统一格式:
json
{
"code": 400,
"message": "结束时间不能早于开始时间"
}
这部分逻辑在:
yudao-framework -> web -> core -> handler -> GlobalExceptionHandler.java
中实现,统一把校验错误转为 CommonResult
的标准结构。
🔧 五、如何在自己的项目中使用?
你只要做到以下几步就能复用:
✅ 1. 在依赖中引入 validation
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
✅ 2. 在 Controller 入参上加上 @Valid
java
@PostMapping("/create")
public CommonResult<?> create(@Valid @RequestBody OrderCreateReqVO reqVO) {
return success(orderService.create(reqVO));
}
✅ 3. 在 VO 上使用注解声明规则
java
public class OrderCreateReqVO {
@NotBlank(message = "订单编号不能为空")
private String orderNo;
@Min(value = 1, message = "数量必须大于0")
private Integer count;
}
✅ 4. 自定义复杂逻辑时用 @AssertTrue
或写自定义校验器
💡 六、额外技巧:可选 vs 必填字段
有些字段并不是必须的,比如 categoryId
。
如果不加任何注解,Spring 就不会校验它。
要让它"可选但有条件约束",可以结合 @AssertTrue
动态判断,比如:
java
@AssertTrue(message = "如果传入分类ID,必须大于0")
@JsonIgnore
public boolean isCategoryIdValid() {
return categoryId == null || categoryId > 0;
}
🧭 七、总结
功能 | 用法 | 示例 |
---|---|---|
非空验证 | @NotNull , @NotBlank |
字符串、数字、对象等 |
范围验证 | @Min , @Max , @Size |
限定长度或数值范围 |
格式验证 | @Email , @Pattern |
邮箱、手机号等 |
自定义逻辑 | @AssertTrue + 布尔方法 |
校验时间、组合条件 |
全局统一返回 | GlobalExceptionHandler | 自动包装友好提示 |
✅ 八、总结一句话
芋道的参数校验机制本质上是:
Spring Boot + Hibernate Validator + 自定义异常处理器
组合实现的一套「自动参数校验 + 友好提示」体系。
你只要在入参 VO 上写好注解,
在 Controller 上加 @Valid
,
剩下的校验、错误提示、格式化返回,系统都会帮你自动完成。