Nodejs Express多个callback错误

我正在使用Nodejs Express自制RBAC系统,基于两个层次:

  • 首先,validation用户是否有正确的angular色来执行此操作。
  • 其次,validation用户是否有正确的计划来执行此操作。

,我创build了一个这样的中间件:

exports.can = function (resource, action) { return function (request, response, next) { action = action || request.method; if (!request.user) { return next(new errors.UnauthorizedError()); } request.user.can(request.user.role, request.user.currentPack, resource, action, function (can, error) { if (error) return next(error); if (!can) { return next(new errors.UnauthorizedError()); } return can; }); return next(); }; }; 

我添加到我的用户模型这个方法:

 const rbac = new RBAC(rbacJson); const pack = new PACK(packJson); schema.method('can', function (role, userPack, resource, action, next) { let can = false; action = action.toUpperCase(); can = rbac.can(role, resource, action); if (can) { can = pack.can(userPack, resource, action, function (can) { return next(can); }); } return next(can); }); 

在我的方法pack.can(…)我需要执行这样一个mongoose的查询:

 PACK.prototype.can = function (pack, resource, action, next) { let can = true; // some sequantial code Trader.count({/* some conditions */}, function (err, count) { if(count == 0) return next(true); return next(false); }); return can; }; 

我的问题是什么时候Mongoose查询的返回是下一个(false),我有这个错误:

  Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11) at ServerResponse.header (/home/invoice/node_modules/express/lib/response.js:730:10) at ServerResponse.send (/home/invoice/node_modules/express/lib/response.js:170:12) at ServerResponse.json (/home/invoice/node_modules/express/lib/response.js:256:15) at ServerResponse.response.apiResponse (/home/invoice/server/config/middlewares/api.js:10:14) at /home/invoice/server/controllers/api/invoice/traders.js:130:21 at /home/invoice/node_modules/mongoose/lib/model.js:3835:16 at /home/invoice/node_modules/mongoose/lib/services/model/applyHooks.js:162:20 at _combinedTickCallback (internal/process/next_tick.js:73:7) at process._tickDomainCallback (internal/process/next_tick.js:128:9) 

经过调查,我发现这个错误可能是由于双重callbackCall:

  • can = pack.can(userPack, resource, action, function (can) { return next(can); });
  • return next(new errors.UnauthorizedError());

但是我不知道如何解决这个问题。 我希望我能很好的解释我的问题。

那么让我们从错误开始:

发送后无法设置标题。

十次中有九次是由于试图向同一个请求发送两个响应而引起的。 标题的引用有点误导,虽然在技术上是正确的。 第二个响应要做的第一件事就是设置一些标头,这将会失败,因为第一个响应已经把它们发送回客户端。

堆栈跟踪为您清楚地指出第二个响应的来源,包括文件名和行号。 更难的是追踪第一个响应,通常我只是在一些额外的控制台日志logging中找出答案。

在这个问题中,你提到你相信你已经find了问题的根源,但是在我看来,这可能只是冰山一angular。 你已经多次使用相同的模式,即使你把它修复在一个可能不够的地方。

在开始之前,我们先从这个开始:

 return next(); 

对于这个例子来说,你是否传递一个错误并不重要,例如return next(err); ,这一点是一样的。 首先调用next() ,它返回undefined 。 然后它从周围的函数返回undefined 。 换句话说,这只是一个方便的速记:

 next(); return; 

我们返回的原因是为了确保在我们调用next()之后没有其他事情发生,我们总是试图确保调用next()是我们在处理程序中做的最后一件事情,不仅仅是因为否则我们可能会有错误我们尝试发送响应两次。

你使用的(反)模式看起来有点像这样:

 obj.doSomething(function() { return next(); // next 1 }); return next(); // next 2 

再一次,你是否在调用next()next(err)并不重要,这一切都是一样的。 关键要注意的是,下一个return只是从传递给doSomething的函数返回。 没有任何事情可以防止下一个被击中。 下一个1和下一个2将被调用。

在你的代码似乎不清楚它是试图同步还是asynchronous,同时使用callback和返回值。 这使得确定“正确”的代码应该是什么样子有点困难。 具体来说, can假设是同步返回的值还是asynchronous传递给callback函数? 我怀疑这是后者,但目前的代码似乎在两者之间撕裂。 重要的是要确保在下一个事件发生之前不要调用next() ,所以如果你正在等待一个数据库查询,那么在这个事件回来之前你不能再调用它。

就我个人而言,我会重命名你的callback,所以他们不是所有的next叫,我觉得很混乱。 当我看到next我期待它是一个Express nextfunction,而不是一个任意的callback。

这是一个猜测,但我build议你的中间件应该是这样的:

 exports.can = function (resource, action) { return function (request, response, next) { action = action || request.method; if (!request.user) { return next(new errors.UnauthorizedError()); } request.user.can(request.user.role, request.user.currentPack, resource, action, function (can, error) { if (error) { next(error); } else if (can) { next(); } else { next(new errors.UnauthorizedError()); } }); // Do not call next() here }; }; 

用户模型的相关部分将是:

 if (can) { pack.can(userPack, resource, action, function (can) { next(can); }); } else { next(can); }