reqオブジェクトを変更せずにリクエスト全体にデータを渡し、すべてのレイヤーに渡す「スレッドセーフ」ソリューションが機能していることがわかりました。
そのため、req の開始時に req をバインドし、次の呼び出しで再利用できるcontinuation-local-storageパッケージがあります。しかし、非同期呼び出しのチェーンを使用しようとすると気分が悪くなります.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/ヘルパーなど) で DI にアクセスできるすべての場所に "requestContext" があります。したがって、「スレッドセーフ」であり、テストが容易であり、すべての「中間」レイヤーを介して req オブジェクトをスローする必要はありません。
同意します。最良で簡単な例ではありませんが、誰かの役に立てば幸いです。