发送后无法设置 headers : 多个中间件执行并发

Can't set headers after they are sent : multiple middleware executions concurency

在我的主要 express.js 配置文件中,我 use 两个自定义错误中间件函数:

const error = require('../middlewares/error');

// catch 404 and forward to error handler
app.use(error.notFound);

// if error is not an instanceOf APIError, convert it.
app.use(error.converter);

我用boom来统一错误信息。这是我的错误中间件:

module.exports = {
  /**
   * Error responder. Send stacktrace only during development
   * @public
   */
  responder: (err, req, res, next) => {
    res.status(err.output.payload.statusCode);
    res.json(err.output.payload);
    res.end();
  },

  /**
   * If error is not a Boom error, convert it.
   * @public
   */
  converter: (err, req, res, next) => {
    if (env !== 'development') {
      delete err.stack;
    }
    if (err.isBoom) {
      return module.exports.responder(err, req, res);
    }
    if (err.name === 'MongoError' && err.code === 11000) {
      const boomedError = boom.conflict('This email already exists');
      boomedError.output.payload.stack = err ? err.stack : undefined;
      return module.exports.responder(boomedError, req, res);
    }
    const boomedError = boom.boomify(err, { statusCode: 422 });
    return module.exports.responder(boomedError, req, res);
  },

  /**
   * Catch 404 and forward to error responder
   * @public
   */
  notFound: (req, res) => {
    const err = boom.notFound('Not Found');
    return module.exports.responder(err, req, res);
  },
};

我的问题是,当我对现有电子邮件执行 "register" 操作时,responder() 被执行了三次。一个是我的 boom.conflict 错误,另一个是 "not found"。 (即使我已经完成 res.end().

这是寄存器逻辑:

exports.register = async (req, res, next) => {
  try {
    validationResult(req).throw();
    const user = new User(req.body);
    const token = generateTokenResponse(user, user.token());
    const userTransformed = user.transform();
    user.tokens.push({ kind: 'jwt', token });
    user.activationId = uuidv1();
    await user.save();
    res.status(httpStatus.CREATED);
    sendValidationEmail(user.activationId);
    return res.json({ user: userTransformed });
  } catch (err) {
    return next(converter(err, req, res, next));
  }
};

user.save() 顺便触发这个:

userSchema.pre('save', async function save(next) {
  try {
    if (!this.isModified('password')) return next();

    const rounds = env === 'test' ? 1 : 10;

    const hash = await bcrypt.hash(this.password, rounds);
    this.password = hash;

    return next();
  } catch (err) {
    return next(converter(err));
  }
});

调用 res.end() 只是告诉响应流您已完成发送数据。在这种情况下您不需要它,因为调用 res.json() 会为您完成。

但是,这与告诉 Express 您已完成请求处理不同。仅仅因为您已发送回复并不一定意味着您没有工作要做。

告诉 Express 您已经完成的方式是不调用 next()。 Express 假定您默认已完成,并且只有在您调用 next().

时才会继续执行 middleware/routing 链

所以这一行:

return next(converter(err, req, res, next));

应该是:

converter(err, req, res, next);

同样,您对 converter() 的另一个调用也不应调用 next()