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

在Node.js中使用Joi验证请求值的唯一性

如何仅通过Joi实现注册接口的Email唯一性校验

要在Joi中纯实现Email唯一性校验,核心是利用Joi的异步自定义校验规则,直接在Joi的校验流程中完成数据库查询校验,无需额外的validateInDatabase函数或mongoose Schema校验。以下是具体实现步骤:

1. 修改Joi校验规则,添加Email异步唯一性校验

在你的userValidation.createUser规则中,给email字段添加异步自定义校验函数,直接查询数据库判断Email是否已存在:

exports.createUser = {
  body: Joi.object().keys({
    email: Joi.string().required().email()
      .custom(async (value, helpers) => {
        // 查询数据库中是否存在该邮箱
        const existingUser = await User.findOne({ email: value });
        if (existingUser) {
          // 返回自定义错误类型,方便后续处理错误消息
          return helpers.error('email.exists', { value });
        }
        return value;
      }, 'Email唯一性校验'), // 第二个参数是校验规则的描述,可选
    password: Joi.string().required().custom(password),
    phoneNumber: Joi.string().min(10).required(),
  }),
};

2. 改造校验中间件,支持异步校验

原有的validate中间件使用的是Joi同步的validate方法,无法处理异步校验逻辑,需要改成使用validateAsync并将中间件设为异步函数:

const validate = (schema) => async (req, res, next) => {
  const validSchema = pick(schema, ["params", "query", "body"]);
  const object = pick(req, Object.keys(validSchema));
  
  try {
    // 使用validateAsync执行异步校验
    const value = await Joi.compile(validSchema)
      .prefs({ errors: { label: "key" }, abortEarly: false })
      .validateAsync(object);
    
    Object.assign(req, value);
    next();
  } catch (error) {
    // 自定义错误消息,把Joi返回的错误类型转换成友好提示
    const errorMessage = error.details
      .map(details => {
        if (details.type === 'email.exists') {
          return `邮箱 ${details.context.value} 已被注册`;
        }
        return details.message;
      })
      .join(", ");
    return res.status(400).json({ error: errorMessage });
  }
};

3. 简化注册控制器,移除冗余校验

现在Email的唯一性已经在Joi中间件中完成校验,控制器里可以去掉validateInDatabase的调用:

exports.register = catchAsync(async (req, res) => {
  try {
    var user = await userService.create(req.body);
    var token = await sendVerification(user);
    return res.status(201).json({ user, token });
  } catch (error) {
    // 处理数据库保存时的其他重复错误(比如手机号重复,若未在Joi中添加校验)
    if (error.code === 11000) {
      return res.status(409).json({ error: "手机号已被注册" });
    }
    return res.status(400).json({ message: error.message });
  }
});

补充说明

  • 如果需要同时校验手机号的唯一性,只需按照Email的方式给phoneNumber字段添加对应的异步自定义校验规则即可。
  • 异步校验会增加一次数据库查询,但能让参数校验逻辑完全统一在Joi流程中,避免分散在多个地方。

内容的提问来源于stack exchange,提问作者John Oliver

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:09:24