我发现工作“线程安全”解决方案可以在整个请求中传递数据而不修改 req 对象并将其传递给所有层。
因此,我们有continuation-local-storage包,它允许我们在 req 开始时绑定 req,然后在下一次调用时重用。但是当您尝试使用异步调用链时感觉很糟糕,CLS 不是“线程安全的”,如果您尝试在少数并发请求期间调用它 - 它会失败并丢失“上下文”。
所以,我们有cls-hooked包,它修复了这个问题,我们可以在请求开始时轻松捕获当前的上下文绑定请求。
有一个使用 awilix(依赖注入框架)的小例子,我们将创建“RequestContext”类并能够在我们需要的任何地方使用它。
让我们创建 RequestContext 类:
module.exports = class RequestContext {
constructor(req, res, dependencies) {
const { myService1, myService2 } = dependencies;
this.req = req;
this.res = res;
}
getMyTestHeader() {
return this.req.headers.testHeader;
}
它是一个简单的“请求包装器”。如果您需要,它会消耗请求、响应和其他依赖项,并提供 getMyTestHeader 以在课堂外使用。
让我们在请求开始时(在所有其他人之前)创建中间件:
//Registering our namespace. creating it one per app
const clsNamespace = require('cls-hooked').createNamespace('my-per-request-session');
app.use((req, res, next) => {
// binding continuation-local-storage (cls-hooked) to request and response
clsNamespace.bind(req);
clsNamespace.bind(res);
clsNamespace.run(() => {
// save here req and res to use it later in requestContext instance, it will alive during request and could be picked up from DI as other dependencies
clsNamespace.set('req', req);
clsNamespace.set('res', res);
next();
});
});
现在,让我们使用 awilix 注册 DI 调用:
container.register({
requestContextProvider: asFunction(dependencies => ({
getCurrentContext: () => {
//retrieve res and req on each DI call "from current request"
//also, clsNamespace should be the same as we registered before, on original code it is also registered using DI container.
const req = dependencies.clsNamespace.get('req');
const res = dependencies.clsNamespace.get('res');
//and return our RequestContext (wrapper for req and res)
return new RequestContext(req, res, dependencies);
},
})),
因此,我们将 requestContextProvider 注册为函数,它将在每个 DI 依赖调用的每个请求上使用外部上下文 (cls) 中的 req 和 res 进行实例化。因此,我们可以在下一个方式使用它(例如在控制器中):
module.exports = (dependencies) => ({
myControllerAction: async (req, res) => {
const {requestContextProvider} = dependencies;
const requestContext = requestContextProvider.getCurrentContext();
//it will return header from req!
const myHeader = requestContext.getMyTestHeader();
res.status(200).json({ myHeader });
},
});
正如你所看到的,我们现在在每个层级(控制器/BLL/DAL/helpers 等)可以访问 DI 的任何地方都有“requestContext”。因此,它是“线程安全的”,易于测试,并且不需要通过所有“中间”层抛出 req 对象。
同意,不是最好和最简单的例子,但希望它可以帮助某人。