You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:53:10