← [ BACK_TO_NOTES ]

Java 请求与领域校验

校验应放在哪一层:输入边界校验与领域不变量如何分工。

#concept #growing #dev #java

[!info] related notes

Java 请求与领域校验

这个概念为什么出现

不可信输入是常态。校验失败应快速、明确;领域不变量应在对象出生与状态迁移时守住,而不是只靠数据库报错。

[!abstract] 一句话理解 输入校验检查形状与值域,领域逻辑守住不变量;身份认证、授权也必须在服务端执行,但不能由字段校验代替。

最小可运行示例

场景:下单命令校验

record PlaceOrder(String skuId, int qty) {
    public PlaceOrder {
        if (skuId == null || skuId.isBlank()) throw new IllegalArgumentException("skuId");
        if (qty <= 0 || qty > 100) throw new IllegalArgumentException("qty");
    }
}

public class PlaceOrderValidationDemo {
    public static void main(String[] args) {
        try {
            new PlaceOrder("", 1);
        } catch (IllegalArgumentException ex) {
            System.out.println("reject: " + ex.getMessage());
        }
        System.out.println(new PlaceOrder("S1", 2));
    }
}
javac PlaceOrderValidationDemo.java && java PlaceOrderValidationDemo

期望含:

reject: skuId
PlaceOrder[skuId=S1, qty=2]

结合场景再看三个关注点

  1. 构造期守不变量。
  2. 错误消息可映射到 API 字段。
  3. 此 record 只检查单条命令的值域;库存、SKU 是否存在和是否有权下单需在用例中核实。Bean Validation 也不取代这些规则。

核心概念与准确模型

1. 分层

层校验什么
控制器类型、必填、格式
应用服务用例前置条件
领域深度不变量
数据库最后防线约束

2. Bean Validation

jakarta.validation 定义注解与 Validator API,需要 provider 与实际校验调用或框架集成;仅添加注解不会自动执行检查。@Valid 用于级联,也需校验流程被触发。不同约束的 null 语义不同,例如 @Size 允许 null,而 @NotNull 不允许。

边界情况与反直觉行为

  1. PATCH 需区分字段缺省与显式 null,不能直接套用完整创建请求的必填规则。
  2. 国际化消息。
  3. 检查库存后再扣减可能发生并发竞争;跨记录/资源约束应由事务、条件更新或数据库约束在写入时保证。

常见误区

[!warning] 常见误区:只做前端校验 可被绕过。

工程实践

  1. 统一错误响应结构。
  2. 复杂规则单测。
  3. 数据库约束双保险。
  4. 避免校验逻辑复制三份却不一致。

本节总结

  • 边界 + 领域双层
  • 失败要明确
  • 框架注解是工具

自测题

  1. 为什么领域层仍要校验?
  2. 校验失败用 400 还是 422?
参考答案
  1. 用例可能被多种入口调用,不变量必须在核心守住。
  2. 400 可用于请求错误;422 表达内容类型和语法可理解但指令无法处理。按 API 契约稳定映射;不能把所有 IllegalArgumentException 都自动认作客户端错误,内部程序错误应另行处理。

延伸阅读与资料来源

资料类型支撑内容
Bean Validation规范/社区注解校验
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本