在你写接口的时候,是否经常看到这样的代码:
if (!body.username || typeof body.username !== 'string') {
ctx.throw(400, '用户名不合法');
}
写起来不难,但随着参数变多、嵌套结构复杂、接口数量增长,这种“手搓式判断”最终只会让代码:
-
不可维护
-
缺乏统一风格
-
校验逻辑四散在项目各处
-
类型无法复用
本篇文章我们来解决一个痛点问题:
如何优雅地进行 接口参数校验 + 类型保护 + 可维护性升级?
一、为什么要重视参数校验?
|
场景 |
后果 |
|---|---|
|
用户提交非法数据 |
接口报错 / 数据错乱 |
|
缺乏类型保护 |
ts 编译过不了 / 跨模块不一致 |
|
参数逻辑分散 |
重复代码 + 出错难排查 |
|
校验与文档割裂 |
无法自动生成 API 规范 / mock 无法复用 |
所以,我们的目标是:
建立一套标准的参数校验机制:既严谨、又可维护、还能自动化。
二、Node.js 中常用校验工具对比
|
工具 |
特点 |
推荐场景 |
|---|---|---|
|
Joi |
成熟稳定、文档齐全、支持嵌套校验 |
Koa / Express 项目通用 |
|
Yup |
类似 Joi,语法更接近表单框架 |
更偏前端 |
|
Zod |
支持 TypeScript 类型推导、极简语法 |
TS 项目首选 |
|
ajv |
支持 JSON Schema,可配合 OpenAPI |
对接协议/后端定义驱动 |
本篇主推组合:Zod + TypeScript
三、Zod 快速入门与实践
安装依赖:
npm i zod
示例:校验一个登录请求参数
import { z } from 'zod';
const LoginSchema = z.object({
username: z.string().min(3),
password: z.string().min(6),
});
你可以在控制器中使用:
const data = LoginSchema.parse(ctx.request.body);
// 若校验失败,会抛出异常,可接中间件捕获
支持嵌套结构校验:
const UserSchema = z.object({
name: z.string(),
address: z.object({
city: z.string(),
zip: z.string().length(6),
}),
});
还能自动推导类型!
type User = z.infer<typeof UserSchema>;
再也不用手动写 interface,且 TS 类型永远与校验保持一致!
四、在 Koa 项目中封装统一中间件
我们可以封装一个验证函数 validateBody(schema):
const validateBody = (schema) => async (ctx, next) => {
try {
ctx.request.validated = await schema.parseAsync(ctx.request.body);
await next();
} catch (err) {
ctx.status = 400;
ctx.body = { error: err.errors };
}
};
使用方式如下:
router.post('/login', validateBody(LoginSchema), async (ctx) => {
const { username } = ctx.request.validated;
ctx.body = { message: `Welcome, ${username}` };
});
五、与前端表单联动 + API 文档自动生成?
-
前端也使用 Zod/Yup → 可复用校验结构
-
使用 ts-to-zod 或 zod-to-json-schema 可导出 OpenAPI 规范
-
后端可以同步生成 mock 数据、类型定义、前后端共享验证逻辑
六、常见误区与实践建议
|
问题 |
建议 |
|---|---|
|
校验 schema 写重复 |
封装 schema 工具,模块化字段 |
|
异常信息太原始 |
自定义 message / 提供 i18n 提示 |
|
校验不统一 |
强制所有接口都走中间件校验 |
|
校验逻辑与类型定义分离 |
使用 Zod / Yup 的类型推导特性,保持统一来源 |
总结:你的 API,不该接受“任何”数据
参数校验是接口服务最基础的防火墙。
如果你希望服务“稳、清晰、好维护”,那就必须做到:
-
明确的 schema
-
一致的中间件
-
自动的类型联动
-
可测试、可扩展、可复用
Zod 是工具,但更重要的是你要建立一个“接口即契约”的意识。
1042

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



