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

Joi v13升级至^17后验证失效及未处理Promise拒绝问题求助

解决Joi v17升级后schema.validate报错及请求挂起问题

我刚帮你定位到问题所在了,主要有两个关键点导致了这个错误和请求挂起的情况:

1. schema.validate的参数传递错误

Joi v17中,schema.validate()的正确签名是schema.validate(value, [options]),第二个参数是可选的配置对象(比如{ abortEarly: false }这类验证选项),而不是你代码里传的schema本身。你写的schema.validate(req.body, schema)把schema当成了选项参数传入,直接触发了Joi内部的Invalid message options错误,这也是服务器报错的根源。

2. 未处理的Promise拒绝

你的CreateUser是async函数,但schema.validate抛出的错误没有被try/catch捕获,加上异步操作里的未处理rejection,导致请求一直挂起,服务器抛出UnhandledPromiseRejectionWarning。

修复后的完整代码

下面是修正后的代码,我还把bcrypt.hash改成了promise形式,保持async/await的统一风格,避免回调嵌套,也更容易处理错误:

const Joi = require("@hapi/joi");
const HttpStatus = require("http-status-codes");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/userModels");
const Helpers = require("../Helpers/helpers");
const dbConfig = require("../config/secret");

module.exports = {
  async CreateUser(req, res) {
    try {
      const schema = Joi.object().keys({
        username: Joi.string()
          .min(5)
          .max(10)
          .required(),
        email: Joi.string()
          .email()
          .required(),
        password: Joi.string()
          .min(5)
          .required()
      });
      // 修正validate参数:第二个参数不传或传配置选项,不是schema
      const { error, value } = schema.validate(req.body);
      if (error && error.details) {
        return res.status(HttpStatus.BAD_REQUEST).json({ msg: error.details });
      }

      const userEmail = await User.findOne({ email: Helpers.lowerCase(req.body.email) });
      if (userEmail) {
        return res
          .status(HttpStatus.CONFLICT)
          .json({ message: "Email already exist" });
      }

      const userName = await User.findOne({ username: Helpers.firstUpper(req.body.username) });
      if (userName) {
        return res
          .status(HttpStatus.CONFLICT)
          .json({ message: "Username already exist" });
      }

      // 改用async/await处理bcrypt.hash,避免回调嵌套
      const hash = await bcrypt.hash(value.password, 10);
      const body = {
        username: Helpers.firstUpper(value.username),
        email: Helpers.lowerCase(value.email),
        password: hash
      };

      const user = await User.create(body);
      const token = jwt.sign({ data: user }, dbConfig.secret, { expiresIn: "5h" });
      res.cookie("auth", token);
      res
        .status(HttpStatus.CREATED)
        .json({ message: "User created successfully", user, token });

    } catch (err) {
      // 统一捕获所有异步错误
      console.error(err);
      if (err.message === "Error hashing password") {
        return res.status(HttpStatus.BAD_REQUEST).json({ message: err.message });
      }
      res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ message: "Error occured" });
    }
  }
};

关键修改点说明

  • 把schema.validate(req.body, schema)改成schema.validate(req.body),如果需要配置验证规则(比如允许所有错误都返回,而不是遇到第一个就停止),可以传{ abortEarly: false }作为第二个参数
  • 用try/catch包裹整个异步逻辑,统一处理所有可能的错误,避免未捕获的Promise拒绝
  • 将bcrypt.hash的回调形式改成await调用,让代码更简洁易读,也便于错误捕获

这样修改后,你的POST请求应该就能正常处理,不会再出现挂起和报错的情况了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 09:07:35