返回博客列表

🚀 3分钟秒懂: Java开发者视角下的Zod 框架

2026-06-24
3 min read
agent

🚀 3分钟秒懂:Java 视角下的 Zod 把 Zod 理解为:DTO 结构 + Bean Validation + TS 类型声明 的三合一工具。它不是 ORM,只是个运行时数据校验器。 -- 1. 概念对齐(Java vs Zod) Java 概念 | Zod 对应写法 | 说明 | | --- | --- | --- | | 定义 DTO 类 | | 结构与规则写在一起 | | / ...

🚀 3分钟秒懂:Java 视角下的 Zod

把 Zod 理解为:DTO 结构 + Bean Validation + TS 类型声明 的三合一工具。它不是 ORM,只是个运行时数据校验器


1. 概念对齐(Java vs Zod)

Java 概念Zod 对应写法说明
定义 DTO 类z.object({...})结构与规则写在一起
@NotBlank / @Sizez.string().min(1)基础格式校验
@Pattern(regexp=...)z.string().regex(...)正则校验
@Positivez.number().positive()数值校验
手动触发校验(抛异常)schema.parse(data)失败直接 throw Exception
校验并获取结果对象schema.safeParse(data)返回 { success: true/false } 不抛异常

⚠️ 核心注意点:Zod 只是规则定义,不会自动拦截!必须显式调用 parse()safeParse() 才会触发校验。


2. 核心大杀器:同步 vs 异步

  • 同步校验 (parse / safeParse):只检查格式(非空、数字、长度、邮箱)。
  • 异步校验 (parseAsync / safeParseAsync):用于检查业务。允许在校验时查数据库、调接口(例如:校验“银行账号是否存在”、“会计期间是否已关闭”)。

3. 高级进阶:复杂业务与类型联动

  • refine / superRefine**:相当于 Java 的自定义校验器(ConstraintValidator)**。适合写财务里的复杂逻辑(如:校验借贷平衡、单据总金额对不对)。
  • z.infer(前端降维打击)
typescript
type Voucher = z.infer<typeof voucherSchema>;

单一真理源:写一份 Schema,自动反推出 TypeScript 的类型声明,再也不用重复写一遍 DTO 类了。


🛠️ 常用 API 备忘表

类别Zod APIJava 对应概念
容器z.object({...})public class MyDto { ... }
z.array(z.string())List<String>
基础z.string().min(1)@NotBlank
z.number().positive()@Positive
z.boolean()Boolean
z.enum(["CNY", "USD"])enum Currency { CNY, USD }
修饰z.string().optional()允许为 undefined (类似 null)

💻 极简 Demo 演示

以财务系统里最经典的“凭证录入(Voucher)”为例,包含结构定义、基础校验、自定义借贷平衡校验(Refine)以及类型反推:

typescript
import { z } from 'zod';

// 1. 定义 Schema(相当于写 Java DTO + 校验注解)
const voucherSchema = z.object({
  id: z.string().uuid(),                         // UUID 格式
  period: z.string().regex(/^\d{4}-\d{2}$/),     // 格式如 "2026-06"
  currency: z.enum(["CNY", "USD"]),              // 枚举限制
  // 分录列表(相当于 List<Entry>),至少 2 条分录
  entries: z.array(
    z.object({
      accountCode: z.string().min(1),            // 科目非空
      debit: z.number().nonnegative(),           // 借方金额 >= 0
      credit: z.number().nonnegative(),          // 贷方金额 >= 0
    })
  ).min(2) 
}).superRefine((data, ctx) => {
  // 复杂的业务校验:借贷平衡(类似 Java 的自定义 ConstraintValidator)
  const totalDebit = data.entries.reduce((sum, e) => sum + e.debit, 0);
  const totalCredit = data.entries.reduce((sum, e) => sum + e.credit, 0);

  if (totalDebit !== totalCredit) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `借贷不平衡!借方:${totalDebit}, 贷方:${totalCredit}`,
      path: ['entries'] // 错误挂在 entries 字段上
    });
  }
});

// 2. 🪄 降维打击:一键反推出 TypeScript 类型(不需要手写 interface 啦!)
type Voucher = z.infer<typeof voucherSchema>;


// 3. 运行期测试数据
const badData = {
  id: "not-a-uuid",
  period: "2026/06",
  currency: "HKD", // 不在枚举内
  entries: [
    { accountCode: "1001", debit: 100, credit: 0 },
    { accountCode: "2001", debit: 0, credit: 90 } // 借贷不平衡
  ]
};

// 4. 执行校验
// 方案 A:直接抛异常(类似 Spring 默认行为)
// voucherSchema.parse(badData); 

// 方案 B:优雅获取结果对象(适合 CLI 或业务层返回 Result)
const result = voucherSchema.safeParse(badData);

if (!result.success) {
  // 打印格式化后的错误信息
  console.log("❌ 校验失败:", result.error.format());
} else {
  // 校验成功后,data 的类型自动安全推导为 Voucher
  const validData: Voucher = result.data; 
  console.log("✅ 校验通过:", validData);
}

返回博客列表
最后更新于 2026-06-24
想法或问题?在 GitHub Issue 下方参与讨论
去评论