Skip to content

Commit 5352bba

Browse files
feature() fix request scoped
1 parent 5cd3448 commit 5352bba

8 files changed

Lines changed: 170 additions & 71 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Controller } from '@nestjs/common/interfaces';
2+
import { ParamProperties } from './context-utils';
3+
4+
export const HANDLER_METADATA_SYMBOL = Symbol.for('handler_metadata:cache');
5+
6+
export interface HandlerMetadata {
7+
argsLength: number;
8+
paramsOptions: (ParamProperties & { metatype?: any })[];
9+
fnHandleResponse: <TResult, TResponse>(
10+
result: TResult,
11+
res: TResponse,
12+
) => any;
13+
}
14+
15+
export class HandlerMetadataStorage<T = any> {
16+
private readonly [HANDLER_METADATA_SYMBOL] = new Map<
17+
string,
18+
HandlerMetadata
19+
>();
20+
21+
set(controller: T, methodName: string, metadata: HandlerMetadata) {
22+
const metadataKey = this.getMetadataKey(controller, methodName);
23+
this[HANDLER_METADATA_SYMBOL].set(metadataKey, metadata);
24+
}
25+
26+
get(controller: T, methodName: string): HandlerMetadata | undefined {
27+
const metadataKey = this.getMetadataKey(controller, methodName);
28+
return this[HANDLER_METADATA_SYMBOL].get(metadataKey);
29+
}
30+
31+
private getMetadataKey(controller: Controller, methodName: string): string {
32+
const ctor = controller.constructor;
33+
const controllerKey = ctor && ctor.name;
34+
return controllerKey + methodName;
35+
}
36+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Global, Module } from '@nestjs/common';
2+
import { AsyncContext } from './async-context';
3+
4+
@Global()
5+
@Module({
6+
providers: [
7+
{
8+
provide: AsyncContext,
9+
useValue: AsyncContext.instance,
10+
},
11+
],
12+
})
13+
export class AsyncHooksModule {}

packages/core/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './async-context';
2+
export * from './async-hooks-module';

packages/core/injector/instance-wrapper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Scope, Type } from '@nestjs/common';
22
import { STATIC_CONTEXT } from './constants';
33
import { Module } from './module';
44

5-
export const INSTANCE_METADATA_SYMBOL = Symbol.for('metadata:cache');
5+
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
66

77
export interface ContextId {
88
readonly id: number;

packages/core/injector/module.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { RuntimeException } from '../errors/exceptions/runtime.exception';
2020
import { UnknownExportException } from '../errors/exceptions/unknown-export.exception';
2121
import { ApplicationReferenceHost } from '../helpers/application-ref-host';
2222
import { ExternalContextCreator } from '../helpers/external-context-creator';
23-
import { AsyncContext } from '../hooks/async-context';
2423
import { Reflector } from '../services/reflector.service';
2524
import { NestContainer } from './container';
2625
import { InstanceWrapper } from './instance-wrapper';
@@ -138,17 +137,6 @@ export class Module {
138137
this.addExternalContextCreator(container.getExternalContextCreator());
139138
this.addModulesContainer(container.getModulesContainer());
140139
this.addApplicationRefHost(container.getApplicationRefHost());
141-
142-
this._providers.set(
143-
AsyncContext.name,
144-
new InstanceWrapper({
145-
name: AsyncContext.name,
146-
metatype: AsyncContext as any,
147-
isResolved: true,
148-
instance: AsyncContext.instance,
149-
host: this,
150-
}),
151-
);
152140
}
153141

154142
public addModuleRef() {

packages/core/middleware/middleware-module.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { HttpServer } from '@nestjs/common';
2+
import { SCOPE_OPTIONS_METADATA } from '@nestjs/common/constants';
23
import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
4+
import { Scope } from '@nestjs/common/interfaces';
35
import {
46
MiddlewareConfiguration,
57
RouteInfo,
@@ -182,6 +184,13 @@ export class MiddlewareModule {
182184
middlewareInstance,
183185
path,
184186
);
187+
188+
const classScope = this.getClassScope(instance);
189+
if (classScope === Scope.REQUEST) {
190+
return bindWithProxy(async (...args: unknown[]) =>
191+
(await instance.resolve())(...args),
192+
);
193+
}
185194
const middleware = await instance.resolve();
186195
bindWithProxy(middleware);
187196
}
@@ -201,4 +210,9 @@ export class MiddlewareModule {
201210
const basePath = validatePath(prefix);
202211
router(basePath + path, proxy);
203212
}
213+
214+
private getClassScope(instance: NestMiddleware): Scope {
215+
const metadata = Reflect.getMetadata(SCOPE_OPTIONS_METADATA, instance);
216+
return metadata && metadata.scope;
217+
}
204218
}

packages/core/router/router-execution-context.ts

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import { FORBIDDEN_MESSAGE } from '../guards/constants';
2424
import { GuardsConsumer } from '../guards/guards-consumer';
2525
import { GuardsContextCreator } from '../guards/guards-context-creator';
2626
import { ContextUtils } from '../helpers/context-utils';
27+
import {
28+
HandlerMetadata,
29+
HandlerMetadataStorage,
30+
} from '../helpers/handler-metadata-storage';
2731
import { STATIC_CONTEXT } from '../injector/constants';
2832
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
2933
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
@@ -48,6 +52,7 @@ export interface ParamProperties {
4852
}
4953

5054
export class RouterExecutionContext {
55+
private readonly handlerMetadataStorage = new HandlerMetadataStorage();
5156
private readonly contextUtils = new ContextUtils();
5257
private readonly responseController: RouterResponseController;
5358

@@ -72,23 +77,18 @@ export class RouterExecutionContext {
7277
requestMethod: RequestMethod,
7378
contextId = STATIC_CONTEXT,
7479
) {
75-
const metadata =
76-
this.contextUtils.reflectCallbackMetadata(
77-
instance,
78-
methodName,
79-
ROUTE_ARGS_METADATA,
80-
) || {};
81-
const keys = Object.keys(metadata);
82-
const argsLength = this.contextUtils.getArgumentsLength(keys, metadata);
83-
const pipes = this.pipesContextCreator.create(
80+
const { argsLength, paramsOptions, fnHandleResponse } = this.getMetadata(
8481
instance,
8582
callback,
83+
methodName,
8684
module,
87-
contextId,
85+
requestMethod,
8886
);
89-
const paramtypes = this.contextUtils.reflectCallbackParamtypes(
87+
const pipes = this.pipesContextCreator.create(
9088
instance,
91-
methodName,
89+
callback,
90+
module,
91+
contextId,
9292
);
9393
const guards = this.guardsContextCreator.create(
9494
instance,
@@ -102,27 +102,10 @@ export class RouterExecutionContext {
102102
module,
103103
contextId,
104104
);
105-
const httpCode = this.reflectHttpStatusCode(callback);
106-
const paramsMetadata = this.exchangeKeysForValues(keys, metadata, module);
107-
const isResponseHandled = paramsMetadata.some(
108-
({ type }) =>
109-
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
110-
);
111-
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
112-
paramsMetadata,
113-
paramtypes,
114-
);
115-
const httpStatusCode = httpCode
116-
? httpCode
117-
: this.responseController.getStatusByMethod(requestMethod);
118105

119106
const fnCanActivate = this.createGuardsFn(guards, instance, callback);
120107
const fnApplyPipes = this.createPipesFn(pipes, paramsOptions);
121-
const fnHandleResponse = this.createHandleResponseFn(
122-
callback,
123-
isResponseHandled,
124-
httpStatusCode,
125-
);
108+
126109
const handler = <TRequest, TResponse>(
127110
args: any[],
128111
req: TRequest,
@@ -152,6 +135,57 @@ export class RouterExecutionContext {
152135
};
153136
}
154137

138+
public getMetadata(
139+
instance: Controller,
140+
callback: (...args: any[]) => any,
141+
methodName: string,
142+
module: string,
143+
requestMethod: RequestMethod,
144+
): HandlerMetadata {
145+
const cacheMetadata = this.handlerMetadataStorage.get(instance, methodName);
146+
if (cacheMetadata) {
147+
return cacheMetadata;
148+
}
149+
const metadata =
150+
this.contextUtils.reflectCallbackMetadata(
151+
instance,
152+
methodName,
153+
ROUTE_ARGS_METADATA,
154+
) || {};
155+
const keys = Object.keys(metadata);
156+
const argsLength = this.contextUtils.getArgumentsLength(keys, metadata);
157+
const paramtypes = this.contextUtils.reflectCallbackParamtypes(
158+
instance,
159+
methodName,
160+
);
161+
const httpCode = this.reflectHttpStatusCode(callback);
162+
const paramsMetadata = this.exchangeKeysForValues(keys, metadata, module);
163+
const isResponseHandled = paramsMetadata.some(
164+
({ type }) =>
165+
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
166+
);
167+
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
168+
paramsMetadata,
169+
paramtypes,
170+
);
171+
const httpStatusCode = httpCode
172+
? httpCode
173+
: this.responseController.getStatusByMethod(requestMethod);
174+
175+
const fnHandleResponse = this.createHandleResponseFn(
176+
callback,
177+
isResponseHandled,
178+
httpStatusCode,
179+
);
180+
const handlerMetadata: HandlerMetadata = {
181+
argsLength,
182+
paramsOptions,
183+
fnHandleResponse,
184+
};
185+
this.handlerMetadataStorage.set(instance, methodName, handlerMetadata);
186+
return handlerMetadata;
187+
}
188+
155189
public reflectHttpStatusCode(callback: (...args: any[]) => any): number {
156190
return Reflect.getMetadata(HTTP_CODE_METADATA, callback);
157191
}

packages/core/router/router-explorer.ts

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { HttpServer } from '@nestjs/common';
2-
import { METHOD_METADATA, PATH_METADATA } from '@nestjs/common/constants';
1+
import { HttpServer, Scope } from '@nestjs/common';
2+
import {
3+
METHOD_METADATA,
4+
PATH_METADATA,
5+
SCOPE_OPTIONS_METADATA,
6+
} from '@nestjs/common/constants';
37
import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
48
import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface';
59
import { Type } from '@nestjs/common/interfaces/type.interface';
@@ -163,7 +167,36 @@ export class RouterExplorer {
163167
const stripSlash = (str: string) =>
164168
str[str.length - 1] === '/' ? str.slice(0, str.length - 1) : str;
165169
const fullPath = stripSlash(basePath) + path;
166-
// TODO:
170+
171+
const classScope = this.getClassScope(instance);
172+
const isRequestScoped = classScope === Scope.REQUEST;
173+
if (isRequestScoped) {
174+
routerMethod(
175+
stripSlash(fullPath) || '/',
176+
async <TRequest, TResponse>(
177+
req: TRequest,
178+
res: TResponse,
179+
next: Function,
180+
) => {
181+
const contextId = { id: 1 }; // asyncId
182+
const contextInstance = await this.injector.loadControllerPerContext(
183+
instance,
184+
this.container.getModules(),
185+
module,
186+
contextId,
187+
);
188+
this.createCallbackProxy(
189+
contextInstance,
190+
contextInstance[methodName],
191+
methodName,
192+
module,
193+
requestMethod,
194+
contextId,
195+
)(req, res, next);
196+
},
197+
);
198+
return;
199+
}
167200
const proxy = this.createCallbackProxy(
168201
instance,
169202
targetCallback,
@@ -172,31 +205,6 @@ export class RouterExplorer {
172205
requestMethod,
173206
);
174207
routerMethod(stripSlash(fullPath) || '/', proxy);
175-
/*routerMethod(
176-
stripSlash(fullPath) || '/',
177-
async <TRequest, TResponse>(
178-
req: TRequest,
179-
res: TResponse,
180-
next: Function,
181-
) => {
182-
const ctx = { id: 2 }; // asyncId
183-
const contextInstance = await this.injector.loadControllerPerContext(
184-
instance,
185-
this.container.getModules(),
186-
module,
187-
ctx,
188-
);
189-
const proxy = this.createCallbackProxy(
190-
contextInstance,
191-
contextInstance[methodName],
192-
methodName,
193-
module,
194-
requestMethod,
195-
ctx,
196-
);
197-
proxy(req, res, next);
198-
},
199-
);*/
200208
}
201209

202210
private createCallbackProxy(
@@ -223,4 +231,9 @@ export class RouterExplorer {
223231
);
224232
return this.routerProxy.createProxy(executionContext, exceptionFilter);
225233
}
234+
235+
private getClassScope(controller: Controller): Scope {
236+
const metadata = Reflect.getMetadata(SCOPE_OPTIONS_METADATA, controller);
237+
return metadata && metadata.scope;
238+
}
226239
}

0 commit comments

Comments
 (0)