
本文详解如何通过 Fastify 的 setErrorHandler 和 Ajv 配置,将默认的验证错误(如 FST_ERR_VALIDATION)统一转换为结构清晰、字段语义明确的自定义 JSON 错误格式,满足 API 规范化与前端友好需求。
本文详解如何通过 fastify 的 `seterrorhandler` 和 ajv 配置,将默认的验证错误(如 fst_err_validation)统一转换为结构清晰、字段语义明确的自定义 json 错误格式,满足 api 规范化与前端友好需求。
在 Fastify 中,默认的 Schema 验证错误由底层 Ajv 生成,并经 Fastify 封装为标准化错误对象(如 FST_ERR_VALIDATION),其结构固定、字段命名(如 statusCode, message)和嵌套方式难以直接适配业务所需的响应契约。若需输出如下的规范化错误体:
{
"message": "Bad request",
"path": "/account/register",
"status": 400,
"timestamp": 1697094987,
"errors": [
{ "key": "email", "value": "This value is not a valid email address." }
]
}
仅靠 schemaErrorFormatter 是不够的——它仅能修改 error.message 字符串内容,无法改变整体响应结构。真正实现格式定制,需协同两步关键配置:
✅ 步骤一:启用 Ajv 的 allErrors 模式
Fastify 默认使用 Ajv 的单错误模式(遇到首个校验失败即终止),而业务中常需返回全部字段错误。需在初始化时显式开启 allErrors: true:
import fastify from 'fastify';
const app = fastify({
logger: true,
ajv: {
customOptions: {
allErrors: true // ⚠️ 注意:官方标注为“可能带来安全风险”,仅在可信内网或严格输入场景下启用
}
}
});
? 安全提示:allErrors: true 可能暴露过多 Schema 结构细节(如字段名、校验规则),生产环境建议结合 removeAdditional: true 或前置数据脱敏策略。
✅ 步骤二:重写 Fastify 全局错误处理器
通过 app.setErrorHandler() 拦截验证类错误(error.validation 存在即为 Ajv 校验失败),并构造符合业务规范的响应体:
app.setErrorHandler((error, request, reply) => {
// 拦截 Ajv 验证错误
if (error.validation) {
const formattedErrors = error.validation.map(err => ({
key: err.params?.missingProperty ||
err.params?.additionalProperty ||
err.dataPath.replace(/\./g, '').replace(/^\/+/, '') ||
'unknown',
value: err.message || 'Validation failed'
}));
return reply
.status(400)
.headers({ 'Content-Type': 'application/json; charset=utf-8' })
.send({
message: 'Bad request',
path: request.url,
status: 400,
timestamp: Date.now(),
errors: formattedErrors
});
}
// 其他未捕获错误(如运行时异常)交由默认逻辑处理
reply.status(500).send({
message: 'Internal server error',
path: request.url,
status: 500,
timestamp: Date.now(),
errors: [{ key: 'server', value: error.message }]
});
});
✅ 示例 Schema 与路由验证
定义含必填字段与类型约束的 Schema,并挂载到路由:
const registerSchema = {
body: {
type: 'object',
required: ['email', 'password'],
properties: {
email: { type: 'string', format: 'email' },
password: { type: 'string', minLength: 6 }
}
}
};
app.post('/account/register', { schema: registerSchema }, async (request, reply) => {
// 业务逻辑
return { success: true };
});
当请求体缺失 email 且 password 过短时,将精准返回:
{
"message": "Bad request",
"path": "/account/register",
"status": 400,
"timestamp": 1718234567890,
"errors": [
{ "key": "email", "value": "must have required property 'email'" },
{ "key": "password", "value": "should NOT be shorter than 6 characters" }
]
}
? 关键注意事项
- 字段映射健壮性:err.params.missingProperty 适用于 required 错误;err.params.additionalProperty 对应多余字段;err.dataPath 需清洗(如 /email → email)以匹配前端字段名。
- 时间戳精度:Date.now() 返回毫秒级时间戳,符合大多数 API 规范;若需秒级,可改为 Math.floor(Date.now() / 1000)。
- 错误分类扩展:可在 if (error.validation) 分支内进一步判断 err.keyword(如 "required", "format")做差异化提示(如将 "format" 错误转为 "This value is not a valid email address.")。
- TypeScript 类型提示:建议为 error.validation 添加类型断言(如 as Ajv.ErrorObject[]),提升开发体验。
通过以上配置,你将彻底掌控 Fastify 验证错误的输出形态,在保持高性能验证的同时,交付专业、一致、易集成的 API 错误响应。











