直接用 ajv 而不是手写 if 校验,因为手写易漏判、难维护、无法复用,而 ajv 是 node.js 生态事实标准,支持多版 json schema、错误清晰、性能可靠。

为什么直接用 ajv 而不是手写 if 校验?
因为接口字段一多,手写校验逻辑极易漏判、难维护,且无法复用校验规则。而 ajv 是目前 Node.js 生态中事实标准的 JSON Schema 验证器,支持 Draft-04/07/2019-09/2020-12,错误提示清晰,性能也经过大量生产验证。
常见错误现象:用 JSON.parse() 后仅检查字段是否存在,但没校验类型、范围、格式(如邮箱、日期字符串)、必填项缺失——这些都得靠 schema 显式定义。
- 必须安装
ajv(v8+ 推荐,v6 已停止维护):npm install ajv - 不要用
ajv-errors插件来“美化”错误——它会让错误结构变复杂,反而干扰调试;原生errorsText()或遍历validation.errors更可控 - schema 必须是合法 JSON Schema 对象,不能是字符串;若从文件读取,请先
JSON.parse()
如何定义并复用一个用户注册的 schema?
真实场景中,同一个 schema 往往用于多个路由(如 POST /register、PUT /user/profile),所以应抽离为模块导出,避免重复定义。
示例 schema(保存为 schema/user-register.json):
{
"type": "object",
"required": ["email", "password", "nickname"],
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8 },
"nickname": { "type": "string", "maxLength": 20 },
"age": { "type": ["integer", "null"], "minimum": 0, "maximum": 150 }
},
"additionalProperties": false
}
-
"additionalProperties": false很关键——防止客户端传入未声明字段(如admin: true)被静默忽略 -
"format": "email"依赖 ajv 的 format 插件,默认不启用;需显式添加:new Ajv({ formats: { email: true } }) - 若需校验手机号、身份证号等自定义格式,用
addFormat()注册正则,别硬塞进 schema
如何在 Express 中拦截并返回结构化验证错误?
验证应在请求体解析之后、业务逻辑之前执行,推荐封装成中间件,统一处理错误格式。
使用一条命令部署ProbeChain Rydberg测试网代理节点。自动注册为Agent(NodeType=1),免gas,支持macOS/Linux/Windows。触发词:/r
实操建议:
- 用
req.body直接传给validate(),不要二次解构或转换(如转成 class 实例)——否则 schema 失效 - 验证失败时,
validate.errors是数组,每个元素含instancePath(如"/email")、message、keyword(如"required") - 不要直接
res.status(400).json(validate.errors)——前端难解析;建议统一包装:{ code: 400, message: "Validation failed", details: errors.map(e => ({ field: e.instancePath.slice(1), reason: e.message })) } - 对
application/json请求才验证;非 JSON 请求(如multipart/form-data)需另写逻辑或跳过
为什么 ajv.compile() 后的验证函数不能跨请求复用?
可以,而且必须复用。每次调用 ajv.compile(schema) 都会生成一个新函数,但该函数本身是纯函数、无状态,可安全缓存并多次调用。
容易踩的坑:
- 在路由 handler 内反复调用
ajv.compile()—— 每次请求都重新编译,浪费 CPU,且无法利用 ajv 内部缓存 - 正确做法:启动时编译一次,导出验证函数:
const validateUserRegister = ajv.compile(userRegisterSchema),后续直接validateUserRegister(req.body) - 如果 schema 动态生成(如根据 query 参数切换),才需要运行时编译,但这种情况极少,且要加 LRU 缓存避免爆炸式编译
- 注意:
ajv实例本身不应跨进程共享;Cluster 模式下每个 worker 自己 new 一个
最常被忽略的是 format 校验的开关和 additionalProperties 的默认行为——它们不开、不设,验证就形同虚设。










