【问题标题】:Mongoose findById possibly sending Express headers?Mongoose findById 可能发送 Express 标头?
【发布时间】:2015-10-08 08:35:19
【问题描述】:

如果条件为真,我正在尝试使用 Mongoose 函数 findById 发送错误。问题是 Mongoose 似乎正在设置 res Express 对象,然后当我尝试自己设置标题时抛出错误。代码如下:

console.log(res.headersSent); // false

Trade.findById(req.body.trade, function (err, trade) {
    if (err) throw err;

    // Ensure user is not making an offer on their own item

    Item.findById(trade.listing, function (err, item) {
        if (err) throw err;

        if (req.decodedId == item.user) {

            console.log(res.headersSent); // true (?)

            return res.status(403).send({
                success: false,
                message: 'You cannot make an offer on your own item'
            })
        } else {
            return;
        }
    })

这是错误的堆栈跟踪:

    false // res.headersSent() before calling Trade.findById()
    POST /api/v2/offer 200 148.799 ms - 162
    true // res.headersSent() after calling Item.findById() and checking error condition
    _http_outgoing.js:335
        throw new Error('Can\'t set headers after they are sent.');
              ^
    Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:700:10)
at ServerResponse.send (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:154:12)
at ServerResponse.json (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:240:15)
at ServerResponse.send (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:142:21)
at /Users/Matt/Dropbox/work/TradeRate/prototype/server/controllers/offers.js:48:40  // LINE THAT CONTAINS return res.status(403).send ...
at /Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/mongoose/lib/query.js:1169:16
at /Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/mongoose/node_modules/kareem/index.js:103:16
at process._tickCallback (node.js:355:11)
18 Jul 15:26:39 - [nodemon] app crashed - waiting for file changes before starting...

什么可能导致此错误? Mongoose API 是否有设置我缺少的响应标头的方面?


编辑:我添加了完整的(更新的)导出路由处理程序,以防有一些上下文可以使问题更清楚。

   // POST /api/offer

exports.createOffer = function (req, res, next) {
console.log(res.headersSent);

Trade.findById(req.body.trade, function (err, trade) {
    if (err) {
        next(err);
        return;
    } // not good to throw from async events, let express' error handling middleware take care of it

    // Ensure user is not making an offer on their own item

    Item.findById(trade.listing, function (err, item) {
        if (err) {
            next(err);
            return;
        }

        if (req.decodedId == item.user) {

            console.log(res.headersSent); // true (?)

            res.status(403).send({
                success: false,
                message: 'You cannot make an offer on your own item'
            });

        }
        // all done with async stuff, pass the request long
        next();
    });

    // If trade is expired, reject the offer

    if (trade.expiresOn < Date.now()) {
        res.status(403).send({
            success: false,
            message: 'This trade has expired and cannot accept new offers'
        });
    }

    // Create new offer and add data

    var newOffer = new Offer();

    newOffer.items = req.body.items;
    newOffer.trade = req.body.trade;

    newOffer.save(function (err, offer) {
        if (err) throw err;
    });

    // Add offer to items in offer

    for (var i = 0; i < req.body.items.length; i++) {
        Item.findById(req.body.items[i], function (err, item) {
            if (err) throw err;

            item.offers.push(newOffer._id);

            item.save(function (err, item) {
                if (err) throw err;
            });
        });
    }

    // Add offer to trade

    trade.offers.push(newOffer._id);

    trade.save(function (err, trade) {
        if (err) throw err;
    });

    return res.send(newOffer);
});

};

【问题讨论】:

标签: node.js express mongoose


【解决方案1】:

全新的答案,忽略我的旧答案,这一切都错了(我已经有一段时间没有使用快递了)。

无论如何,问题是您调用的异步函数会立即返回,所以当您调用return res.send(newOffer); 时,您会在这些回调返回之前执行此操作。所以你在你之前回来了

  • 检查用户是否尝试在他们自己的项目上创建报价
  • 将新的商品 ID 添加到商品中
  • 保存任何更改

另一个问题是您的循环可能会严重失败。无法保证您会按顺序推送这些项目,因为 findByIdsave 是异步的,它们会立即返回并且可以按任何顺序执行。另外,每次推送后根本没有理由保存。您需要等待每个 findById 在继续循环之前返回(因此您不能使用基本的 for 循环,很可能是回调)或更正确地,只需使用 mongoose 更新查询一次完成所有操作(您无需加载商品即可向其推送优惠,只需使用$push)

在 express 中处理所有这些问题的最佳方法是使用中间件。所以把你的代码改成这个(我添加了一个对http-errors的依赖以使错误处理更容易。

我假设您使用的是最新版本的 express:

报价路线

var httpError = require('http-error') // needed for ezpz http errors
var express = require('express'); // needed for express.Router()


// middleware that loads the trade
function loadTrade(req, res, next) {
  Trade.findById(req.body.trade, function (err, trade) {
    req.trade = trade;
    next(err, trade);
  })
}

// middlware that checks expiration
function checkExpired(req, res, next) {
  if(req.trade.expiresOn < Date.now())
    next(httpError(403, 'This trade has expired and cannot accept new offers'));
  else next();
}

// middleware makes sure the user isn't making an offer on their own item
function checkIsOwner(req, res, next) {
  Trade.findById(req.trade.listing)
       .select('user')
       .exec(function(err, listing) {
          if (err) next(err)
          else if (listing.user == req.decodedId) next(httpError(403, 'You can not make an offer on your own item'))
          else next();
        })
}

// now we can create an offer
function createOffer(req, res, next) {
  // req.trade was loaded and validated by our middleware
  // if next(err) was called at any point this function wouldn't be called
  var trade = req.trade;
  Offer.create({trade: trade._id, items: req.body.items}, function (err, offer) {
    if (err) {
      next(err); // we only call next to trigger the error handler
      return;
    }
    // now push the new offer id to all the items
    Item.update({$in: req.body.items}, {$push: offer._id}, function (err, offer) {
      if (err) next(err)
      else res.json(newOffer);
    })
  });
}


exports.createOffer = express.Router()
  .post(loadTrade)
  .post(checkExpired)
  .post(checkIsOwner)
  .post(createOffer);

为了处理错误,我会在你设置完所有路由后添加这个(你有 app.post('/api/v2/offer', ....) 的东西:

app.use('/api/v2/*', function(err, req, res, next) {
   res.status(err.status || 500).json({ success: false, message: err.message });
});

现在,每当您调用 next(err) 时,都会调用此错误处理程序并发送状态代码和错误消息。

【讨论】:

  • 对不起,我不熟悉,我的快递员会是什么样子?
  • 我尝试了你的代码,但它没有用,我用完整的导出函数更新了我的问题 - 如果有什么你想看的可以让我更容易解决这个问题,请告诉我.谢谢!
  • 将路由拆分成单独的函数解决了我的问题,谢谢!
  • 没问题!需要明确的是,尽管这恰好是最简洁的表达方式,但您可以将所有内容放在一个函数中,但您需要嵌套所有回调,以便一切都以正确的顺序发生
猜你喜欢
  • 1970-01-01
  • 2019-12-05
  • 1970-01-01
  • 2021-01-03
  • 2014-11-09
  • 2020-02-03
  • 2019-05-12
  • 2018-09-16
  • 2018-06-11
相关资源
最近更新 更多