Hapi + Joi自定义扩展实现异步验证的方法咨询
Hapi + Joi 自定义扩展实现异步验证
问题场景
你在Hapi服务中定义了带自定义Joi类型的路由,自定义类型的验证逻辑是异步的(比如查询数据库校验用户存在性),但发现异步验证还没完成,路由的handler就已经被调用了。
你的路由代码:
{ method: 'GET', path: '/user/{userId}', options: { validate: { params: j.object({ userId: j.user().required(), // 自定义user类型 }), }, }, handler: getUserHandler, }
自定义Joi扩展代码:
const joiExtended = Joi.extend( { type: 'user', base: Joi.string(), validate: async (value, helpers) => { // 异步DB查询逻辑 }, ... }, )
之前尝试的两种方法均未成功:
- 试图用
base: Joi.any()结合external函数实现,但不知道如何在扩展中配置; - 想使用
validateAsync,但不清楚如何让Hapi路由启用该异步验证方式。
正确实现方式
1. 修正Joi自定义扩展
确保自定义类型的validate方法返回Promise(async函数天然返回Promise),并正确处理验证结果:
const joiExtended = Joi.extend({ type: 'user', base: Joi.string(), // 异步验证逻辑 async validate(value, helpers) { // 示例:异步查询数据库检查用户是否存在 const userExists = await yourDbClient.query('SELECT id FROM users WHERE id = ?', [value]); if (!userExists.length) { // 返回错误信息 return helpers.error('user.notFound', { value }); } // 验证通过,返回原值 return value; }, // 自定义错误提示 messages: { 'user.notFound': '用户ID {#value} 不存在' } });
2. 配置Hapi路由的异步验证
Hapi默认会用同步方式执行Joi验证,因此需要在路由的validate配置中,手动使用异步验证函数调用schema.validateAsync():
{ method: 'GET', path: '/user/{userId}', options: { validate: { // 用异步函数包裹验证逻辑 async params(params) { const schema = joiExtended.object({ userId: joiExtended.user().required() }); // 调用Joi的异步验证方法,Hapi会等待该Promise完成 const validated = await schema.validateAsync(params); return validated; } } }, handler: getUserHandler }
关于external方法的补充
如果想通过external实现异步验证并封装到自定义类型中,可以在扩展的rules里定义自定义规则,或者直接在validate方法中实现(如上例)。如果直接使用external,也可以不用自定义类型,直接在schema中链式调用:
const userSchema = joiExtended.object({ userId: Joi.string() .external(async (value) => { const userExists = await yourDbClient.query('SELECT id FROM users WHERE id = ?', [value]); if (!userExists.length) { throw new Error(`用户ID ${value} 不存在`); } return value; }) .required() });
然后同样在路由的异步验证函数中调用userSchema.validateAsync(params)即可。
内容的提问来源于stack exchange,提问作者Madbreaks
相关产品推荐
相关产品推荐

