gpt4 book ai didi

node.js - Nodejs Express 多次回调错误

转载 作者:太空宇宙 更新时间:2023-11-03 23:23:57 25 4
gpt4 key购买 nike

我正在使用 Nodejs Express 开发一个自制的 RBAC 系统,该系统基于两个级别:

  • 首先,验证用户是否具有执行此操作的正确角色。
  • 其次,验证用户是否有执行此操作的正确计划。

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

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 查询的返回是 next(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)

经过排查,发现错误可能是由于双重回调Call导致的:

  • can = pack.can(userPack, 资源, 操作, 函数 (can) {
    返回下一个(可以);
    });
  • 返回下一个(新错误.UnauthorizedError());

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

最佳答案

那么让我们从错误开始:

Can't set headers after they are sent.

十分之九的情况是由于尝试向同一请求发送两个响应而导致的。对 header 的引用有点误导,尽管在技术上是正确的。第二个响应将尝试做的第一件事是设置一些 header ,这将失败,因为第一个响应已经将它们发送回客户端。

堆栈跟踪可以清楚地指示第二个响应的来源,包括文件名和行号。更困难的是追踪第一个响应,通常我只是添加一些额外的控制台日志记录来找出它。

在问题中,您提到您相信您已经找到了问题的根源,但在我看来,这可能只是冰山一角。您已多次使用相同的模式,即使您在一处修复它也可能不够。

在开始之前,我们先来说一下:

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),这并不重要,其结果都差不多。需要注意的关键是 next 1 的 return 只是从传递给 doSomething 的函数返回。它没有做任何事情来防止接下来的两个被击中。 next 1 和 next 2 都会被调用。

在某些地方,您的代码似乎不清楚它是尝试同步还是异步,同时使用回调和返回值。这使得确定“正确”代码应该是什么样子变得有点困难。具体来说,can的值是应该同步返回还是异步传递给回调?我怀疑是后者,但当前的代码似乎在两者之间左右为难。请务必确保在准备好发生下一件事情之前不要调用 next(),因此,如果您正在等待数据库查询,则在该查询返回之前不得调用 next

就我个人而言,我会重命名您的回调,这样它们就不会全部被称为 next,我发现这真的很令人困惑。当我看到 next 时,我希望它是一个 Express next 函数,而不仅仅是一个任意回调。

这有点猜测,但我建议你的中间件应该看起来像这样:

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);
}

关于node.js - Nodejs Express 多次回调错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46614320/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com