mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
feature(@nestjs) enhance execution context, create host
This commit is contained in:
@@ -84,12 +84,12 @@ gulp.task('move', function() {
|
||||
const getDirs = (base) => getFolders(base)
|
||||
.map((path) => `${base}/${path}`);
|
||||
|
||||
const examplesDirs = getDirs('examples');
|
||||
const examplesDirs = getDirs('sample');
|
||||
const integrationDirs = getDirs('integration');
|
||||
const directories = examplesDirs.concat(integrationDirs);
|
||||
|
||||
let stream = gulp
|
||||
.packages(['node_modules/@nestjs/**/*']);
|
||||
.src(['node_modules/@nestjs/**/*']);
|
||||
|
||||
directories.forEach((dir) => {
|
||||
stream = stream.pipe(gulp.dest(dir + '/node_modules/@nestjs'));
|
||||
|
||||
@@ -8,23 +8,23 @@ import { ApplicationModule } from './../src/app.module';
|
||||
import { FastifyAdapter } from '@nestjs/core/adapters/fastify-adapter';
|
||||
import { ExpressAdapter } from '@nestjs/core/adapters/express-adapter';
|
||||
import { HelloService } from '../src/hello/hello.service';
|
||||
import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface';
|
||||
|
||||
describe('Hello world (fastify adapter)', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
let app: INestApplication & INestFastifyApplication
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [ApplicationModule],
|
||||
}).compile();
|
||||
|
||||
server = fastify();
|
||||
app = module.createNestApplication(new FastifyAdapter(server));
|
||||
app = module.createNestApplication(new FastifyAdapter());
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`/GET`, () => {
|
||||
return server
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/hello',
|
||||
@@ -33,7 +33,7 @@ describe('Hello world (fastify adapter)', () => {
|
||||
});
|
||||
|
||||
it(`/GET (Promise/async)`, () => {
|
||||
return server
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/hello/async',
|
||||
@@ -42,7 +42,7 @@ describe('Hello world (fastify adapter)', () => {
|
||||
});
|
||||
|
||||
it(`/GET (Observable stream)`, () => {
|
||||
return server
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/hello/stream',
|
||||
|
||||
@@ -10,14 +10,14 @@ const RETURN_VALUE = 'test';
|
||||
|
||||
@Injectable()
|
||||
export class OverrideInterceptor {
|
||||
intercept(data, context, stream) {
|
||||
intercept(context, stream) {
|
||||
return of(RETURN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TransformInterceptor {
|
||||
intercept(data, context, stream) {
|
||||
intercept(context, stream) {
|
||||
return stream.pipe(map(data => ({ data })));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ export {
|
||||
INestApplicationContext,
|
||||
HttpServer,
|
||||
HttpServerFactory,
|
||||
ArgumentsHost,
|
||||
INestExpressApplication,
|
||||
INestFastifyApplication,
|
||||
} from './interfaces';
|
||||
export * from './interceptors';
|
||||
export * from './services/logger.service';
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Observable } from 'rxjs/Observable';
|
||||
import { MulterOptions } from '../interfaces/external/multer-options.interface';
|
||||
import { mixin } from '../decorators/core/component.decorator';
|
||||
import { transformException } from './multer/multer.utils';
|
||||
import { ExecutionContext } from './../interfaces';
|
||||
|
||||
export function FileInterceptor(fieldName: string, options?: MulterOptions) {
|
||||
return mixin(
|
||||
@@ -11,18 +12,23 @@ export function FileInterceptor(fieldName: string, options?: MulterOptions) {
|
||||
readonly upload = multer(options);
|
||||
|
||||
async intercept(
|
||||
request,
|
||||
context,
|
||||
context: ExecutionContext,
|
||||
stream$: Observable<any>,
|
||||
): Promise<Observable<any>> {
|
||||
const ctx = context.switchToHttp();
|
||||
|
||||
await new Promise((resolve, reject) =>
|
||||
this.upload.single(fieldName)(request, request.res, err => {
|
||||
if (err) {
|
||||
const error = transformException(err);
|
||||
return reject(error);
|
||||
}
|
||||
resolve();
|
||||
}),
|
||||
this.upload.single(fieldName)(
|
||||
ctx.getRequest(),
|
||||
ctx.getResponse(),
|
||||
err => {
|
||||
if (err) {
|
||||
const error = transformException(err);
|
||||
return reject(error);
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
),
|
||||
);
|
||||
return stream$;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NestInterceptor } from './../interfaces/features/nest-interceptor.inter
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { MulterOptions } from '../interfaces/external/multer-options.interface';
|
||||
import { transformException } from './multer/multer.utils';
|
||||
import { ExecutionContext } from './../interfaces';
|
||||
|
||||
export function FilesInterceptor(
|
||||
fieldName: string,
|
||||
@@ -13,18 +14,23 @@ export function FilesInterceptor(
|
||||
readonly upload = multer(options);
|
||||
|
||||
async intercept(
|
||||
request,
|
||||
context,
|
||||
context: ExecutionContext,
|
||||
stream$: Observable<any>,
|
||||
): Promise<Observable<any>> {
|
||||
const ctx = context.switchToHttp();
|
||||
|
||||
await new Promise((resolve, reject) =>
|
||||
this.upload.array(fieldName, maxCount)(request, request.res, err => {
|
||||
if (err) {
|
||||
const error = transformException(err);
|
||||
return reject(error);
|
||||
}
|
||||
resolve();
|
||||
}),
|
||||
this.upload.array(fieldName, maxCount)(
|
||||
ctx.getRequest(),
|
||||
ctx.getResponse(),
|
||||
err => {
|
||||
if (err) {
|
||||
const error = transformException(err);
|
||||
return reject(error);
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
),
|
||||
);
|
||||
return stream$;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export interface ExceptionFilter<T = any, R = any> {
|
||||
catch(exception: T, response: R);
|
||||
import { ArgumentsHost } from './../features/arguments-host.interface';
|
||||
|
||||
export interface ExceptionFilter<T = any> {
|
||||
catch(exception: T, host: ArgumentsHost);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ArgumentsHost } from './../features/arguments-host.interface';
|
||||
|
||||
export interface RpcExceptionFilter<T = any, R = any> {
|
||||
catch(exception: T): Observable<R>;
|
||||
catch(exception: T, host: ArgumentsHost): Observable<R>;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export interface WsExceptionFilter<T = any, R = any> {
|
||||
catch(exception: T, client: R);
|
||||
import { ArgumentsHost } from './../features/arguments-host.interface';
|
||||
|
||||
export interface WsExceptionFilter<T = any> {
|
||||
catch(exception: T, host: ArgumentsHost);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
export interface HttpArgumentsHost {
|
||||
getRequest<T = any>(): T;
|
||||
getResponse<T = any>(): T;
|
||||
}
|
||||
|
||||
export interface WsArgumentsHost {
|
||||
getData<T = any>(): T;
|
||||
getClient<T = any>(): T;
|
||||
}
|
||||
|
||||
export interface RpcArgumentsHost {
|
||||
getData<T = any>(): T;
|
||||
}
|
||||
|
||||
export interface ArgumentsHost {
|
||||
getArgs<T extends Array<any> = any[]>(): T;
|
||||
getArgByIndex<T = any>(index: number): T;
|
||||
switchToRpc(): RpcArgumentsHost;
|
||||
switchToHttp(): HttpArgumentsHost;
|
||||
switchToWs(): WsArgumentsHost;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { ExecutionContext } from './execution-context.interface';
|
||||
|
||||
export interface CanActivate {
|
||||
canActivate(
|
||||
request,
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean>;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Type } from './../index';
|
||||
import { ArgumentsHost } from './arguments-host.interface';
|
||||
|
||||
export interface ExecutionContext {
|
||||
export interface ExecutionContext extends ArgumentsHost {
|
||||
getClass<T = any>(): Type<T>;
|
||||
getHandler(): Function;
|
||||
getArgs<T extends Array<any> = any[]>(): T;
|
||||
getArgByIndex<T = any>(index: number): T;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ExecutionContext } from './execution-context.interface';
|
||||
|
||||
export interface NestInterceptor<T = any, R = any> {
|
||||
intercept(
|
||||
dataOrRequest,
|
||||
context: ExecutionContext,
|
||||
stream$: Observable<T>,
|
||||
): Observable<R> | Promise<Observable<R>>;
|
||||
|
||||
@@ -24,4 +24,7 @@ export * from './features/nest-interceptor.interface';
|
||||
export * from './features/custom-route-param-factory.interface';
|
||||
export * from './modules/dynamic-module.interface';
|
||||
export * from './http/http-server.interface';
|
||||
export * from './http/http-server-factory.interface';
|
||||
export * from './http/http-server-factory.interface';
|
||||
export * from './features/arguments-host.interface';
|
||||
export * from './nest-express-application.interface';
|
||||
export * from './nest-fastify-application.interface';
|
||||
@@ -14,10 +14,10 @@ export interface INestFastifyApplication {
|
||||
* @returns this
|
||||
*/
|
||||
useStaticAssets(options: {
|
||||
root: string,
|
||||
prefix: string,
|
||||
setHeaders: Function,
|
||||
send: any,
|
||||
root: string;
|
||||
prefix: string;
|
||||
setHeaders: Function;
|
||||
send: any;
|
||||
}): this;
|
||||
|
||||
/**
|
||||
@@ -26,4 +26,52 @@ export interface INestFastifyApplication {
|
||||
* @returns this
|
||||
*/
|
||||
setViewEngine(options: any): this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper function around native `fastify.inject()` method.
|
||||
* @returns void
|
||||
*/
|
||||
inject(opts: HTTPInjectOptions | string): Promise<HTTPInjectResponse>;
|
||||
}
|
||||
|
||||
/** Reference: https://github.com/fastify/fastify */
|
||||
export type HTTPMethod =
|
||||
| 'DELETE'
|
||||
| 'GET'
|
||||
| 'HEAD'
|
||||
| 'PATCH'
|
||||
| 'POST'
|
||||
| 'PUT'
|
||||
| 'OPTIONS';
|
||||
|
||||
/**
|
||||
* Fake http inject options
|
||||
*/
|
||||
export interface HTTPInjectOptions {
|
||||
url: string;
|
||||
method?: HTTPMethod;
|
||||
authority?: string;
|
||||
headers?: object;
|
||||
remoteAddress?: string;
|
||||
payload?: string | object | Buffer | any;
|
||||
simulate?: {
|
||||
end?: boolean;
|
||||
split?: boolean;
|
||||
error?: boolean;
|
||||
close?: boolean;
|
||||
};
|
||||
validate?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake http inject response
|
||||
*/
|
||||
export interface HTTPInjectResponse {
|
||||
raw: any;
|
||||
headers: object;
|
||||
statusCode: number;
|
||||
statusMessage: string;
|
||||
payload: string;
|
||||
rawPayload: Buffer;
|
||||
trailers: object;
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ export interface WebSocketAdapter<T = any> {
|
||||
}[],
|
||||
process: (data) => Observable<any>,
|
||||
);
|
||||
close(server);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,10 @@ export class FastifyAdapter {
|
||||
return this.instance.register(...args);
|
||||
}
|
||||
|
||||
inject(...args) {
|
||||
return this.instance.inject(...args);
|
||||
}
|
||||
|
||||
close() {
|
||||
return this.instance.close();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/ex
|
||||
import { isEmpty, isObject } from '@nestjs/common/utils/shared.utils';
|
||||
import { InvalidExceptionFilterException } from '../errors/exceptions/invalid-exception-filter.exception';
|
||||
import { HttpException } from '@nestjs/common';
|
||||
import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface';
|
||||
|
||||
export class ExceptionsHandler {
|
||||
private static readonly logger = new Logger(ExceptionsHandler.name);
|
||||
@@ -11,8 +12,8 @@ export class ExceptionsHandler {
|
||||
|
||||
constructor(private readonly applicationRef: HttpServer) {}
|
||||
|
||||
public next(exception: Error | HttpException | any, response) {
|
||||
if (this.invokeCustomFilters(exception, response)) return;
|
||||
public next(exception: Error | HttpException | any, ctx: ArgumentsHost) {
|
||||
if (this.invokeCustomFilters(exception, ctx)) return;
|
||||
|
||||
if (!(exception instanceof HttpException)) {
|
||||
const body = {
|
||||
@@ -20,7 +21,7 @@ export class ExceptionsHandler {
|
||||
message: messages.UNKNOWN_EXCEPTION_MESSAGE,
|
||||
};
|
||||
const statusCode = 500;
|
||||
this.applicationRef.reply(response, body, statusCode);
|
||||
this.applicationRef.reply(ctx.getArgByIndex(1), body, statusCode);
|
||||
if (this.isExceptionObject(exception)) {
|
||||
return ExceptionsHandler.logger.error(
|
||||
exception.message,
|
||||
@@ -36,7 +37,11 @@ export class ExceptionsHandler {
|
||||
statusCode: exception.getStatus(),
|
||||
message: res,
|
||||
};
|
||||
this.applicationRef.reply(response, message, exception.getStatus());
|
||||
this.applicationRef.reply(
|
||||
ctx.getArgByIndex(1),
|
||||
message,
|
||||
exception.getStatus(),
|
||||
);
|
||||
}
|
||||
|
||||
public setCustomFilters(filters: ExceptionFilterMetadata[]) {
|
||||
|
||||
@@ -7,23 +7,24 @@ import {
|
||||
isEmpty,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { CanActivate, HttpStatus, ExecutionContext } from '@nestjs/common';
|
||||
import { CanActivate, HttpStatus } from '@nestjs/common';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FORBIDDEN_MESSAGE } from './constants';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context.host';
|
||||
|
||||
export class GuardsConsumer {
|
||||
public async tryActivate(
|
||||
guards: CanActivate[],
|
||||
data,
|
||||
args: any[],
|
||||
instance: Controller,
|
||||
callback: (...args) => any,
|
||||
): Promise<boolean> {
|
||||
if (!guards || isEmpty(guards)) {
|
||||
return true;
|
||||
}
|
||||
const context = this.createContext(instance, callback);
|
||||
const context = this.createContext(args, instance, callback);
|
||||
for (const guard of guards) {
|
||||
const result = guard.canActivate(data, context);
|
||||
const result = guard.canActivate(context);
|
||||
if (await this.pickResult(result)) {
|
||||
continue;
|
||||
}
|
||||
@@ -33,13 +34,15 @@ export class GuardsConsumer {
|
||||
}
|
||||
|
||||
public createContext(
|
||||
args: any[],
|
||||
instance: Controller,
|
||||
callback: (...args) => any,
|
||||
): ExecutionContext {
|
||||
return {
|
||||
parent: instance.constructor,
|
||||
handler: callback,
|
||||
};
|
||||
): ExecutionContextHost {
|
||||
return new ExecutionContextHost(
|
||||
args,
|
||||
instance.constructor as any,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
|
||||
public async pickResult(
|
||||
|
||||
51
packages/core/helpers/execution-context.host.ts
Normal file
51
packages/core/helpers/execution-context.host.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { ExecutionContext } from '@nestjs/common';
|
||||
import { Type } from '@nestjs/common/interfaces';
|
||||
import {
|
||||
RpcArgumentsHost,
|
||||
WsArgumentsHost,
|
||||
HttpArgumentsHost,
|
||||
} from '@nestjs/common/interfaces/features/arguments-host.interface';
|
||||
|
||||
export class ExecutionContextHost implements ExecutionContext {
|
||||
constructor(
|
||||
private readonly args: any[],
|
||||
private readonly constructorRef: Type<any> = null,
|
||||
private readonly handler: Function = null,
|
||||
) {}
|
||||
|
||||
getClass<T = any>(): Type<T> {
|
||||
return this.constructorRef;
|
||||
}
|
||||
|
||||
getHandler(): Function {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
getArgs<T extends Array<any> = any[]>(): T {
|
||||
return this.args as T;
|
||||
}
|
||||
|
||||
getArgByIndex<T = any>(index: number): T {
|
||||
return this.args[index] as T;
|
||||
}
|
||||
|
||||
switchToRpc(): RpcArgumentsHost {
|
||||
return Object.assign(this, {
|
||||
getData: () => this.getArgByIndex(0),
|
||||
});
|
||||
}
|
||||
|
||||
switchToHttp(): HttpArgumentsHost {
|
||||
return Object.assign(this, {
|
||||
getRequest: () => this.getArgByIndex(0),
|
||||
getResponse: () => this.getArgByIndex(1),
|
||||
});
|
||||
}
|
||||
|
||||
switchToWs(): WsArgumentsHost {
|
||||
return Object.assign(this, {
|
||||
getClient: () => this.getArgByIndex(0),
|
||||
getData: () => this.getArgByIndex(1),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,9 @@ export class ExternalContextCreator {
|
||||
module,
|
||||
);
|
||||
return async (...args) => {
|
||||
const [req] = args;
|
||||
const canActivate = await this.guardsConsumer.tryActivate(
|
||||
guards,
|
||||
req,
|
||||
args,
|
||||
instance,
|
||||
callback,
|
||||
);
|
||||
@@ -44,7 +43,7 @@ export class ExternalContextCreator {
|
||||
const handler = () => callback.apply(instance, args);
|
||||
return await this.interceptorsConsumer.intercept(
|
||||
interceptors,
|
||||
req,
|
||||
args,
|
||||
instance,
|
||||
callback,
|
||||
handler,
|
||||
|
||||
@@ -7,16 +7,17 @@ import {
|
||||
isEmpty,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { HttpStatus, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
||||
import { HttpStatus, NestInterceptor } from '@nestjs/common';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { defer } from 'rxjs/observable/defer';
|
||||
import { fromPromise } from 'rxjs/observable/fromPromise';
|
||||
import { take, switchMap } from 'rxjs/operators';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context.host';
|
||||
|
||||
export class InterceptorsConsumer {
|
||||
public async intercept(
|
||||
interceptors: NestInterceptor[],
|
||||
dataOrRequest: any,
|
||||
args: any[],
|
||||
instance: Controller,
|
||||
callback: (...args) => any,
|
||||
next: () => Promise<any>,
|
||||
@@ -24,24 +25,26 @@ export class InterceptorsConsumer {
|
||||
if (!interceptors || isEmpty(interceptors)) {
|
||||
return await await next();
|
||||
}
|
||||
const context = this.createContext(instance, callback);
|
||||
const context = this.createContext(args, instance, callback);
|
||||
const start$ = defer(() => this.transformDeffered(next));
|
||||
const result$ = await interceptors.reduce(
|
||||
async (stream$, interceptor) =>
|
||||
await interceptor.intercept(dataOrRequest, context, await stream$),
|
||||
await interceptor.intercept(context, await stream$),
|
||||
Promise.resolve(start$),
|
||||
);
|
||||
return await result$.toPromise();
|
||||
}
|
||||
|
||||
public createContext(
|
||||
args: any[],
|
||||
instance: Controller,
|
||||
callback: (...args) => any,
|
||||
): ExecutionContext {
|
||||
return {
|
||||
parent: instance.constructor,
|
||||
handler: callback,
|
||||
};
|
||||
): ExecutionContextHost {
|
||||
return new ExecutionContextHost(
|
||||
args,
|
||||
instance.constructor as any,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
|
||||
public transformDeffered(next: () => Promise<any>): Observable<any> {
|
||||
|
||||
@@ -272,6 +272,11 @@ export class NestApplication extends NestApplicationContext
|
||||
return this;
|
||||
}
|
||||
|
||||
public inject(...args) {
|
||||
const adapter = this.httpAdapter as FastifyAdapter;
|
||||
return adapter.inject && adapter.inject(...args);
|
||||
}
|
||||
|
||||
public enableCors(options?: CorsOptions): this {
|
||||
this.httpAdapter.use(cors(options));
|
||||
return this;
|
||||
|
||||
@@ -94,7 +94,7 @@ export class RouterExecutionContext {
|
||||
|
||||
return async (req, res, next) => {
|
||||
const args = this.createNullArray(argsLength);
|
||||
fnCanActivate && (await fnCanActivate(req));
|
||||
fnCanActivate && (await fnCanActivate([res, res]));
|
||||
|
||||
const handler = async () => {
|
||||
fnApplyPipes && (await fnApplyPipes(args, req, res, next));
|
||||
@@ -102,7 +102,7 @@ export class RouterExecutionContext {
|
||||
};
|
||||
const result = await this.interceptorsConsumer.intercept(
|
||||
interceptors,
|
||||
req,
|
||||
[req, res],
|
||||
instance,
|
||||
callback,
|
||||
handler,
|
||||
@@ -213,10 +213,10 @@ export class RouterExecutionContext {
|
||||
instance: Controller,
|
||||
callback: (...args) => any,
|
||||
) {
|
||||
const canActivateFn = async req => {
|
||||
const canActivateFn = async (args: any[]) => {
|
||||
const canActivate = await this.guardsConsumer.tryActivate(
|
||||
guards,
|
||||
req,
|
||||
args,
|
||||
instance,
|
||||
callback,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context.host';
|
||||
|
||||
export type RouterProxyCallback = (req?, res?, next?) => void;
|
||||
|
||||
@@ -8,12 +9,13 @@ export class RouterProxy {
|
||||
exceptionsHandler: ExceptionsHandler,
|
||||
) {
|
||||
return (req, res, next) => {
|
||||
const host = new ExecutionContextHost([req, res]);
|
||||
try {
|
||||
Promise.resolve(targetCallback(req, res, next)).catch(e => {
|
||||
exceptionsHandler.next(e, res);
|
||||
exceptionsHandler.next(e, host);
|
||||
});
|
||||
} catch (e) {
|
||||
exceptionsHandler.next(e, res);
|
||||
exceptionsHandler.next(e, host);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -23,12 +25,13 @@ export class RouterProxy {
|
||||
exceptionsHandler: ExceptionsHandler,
|
||||
) {
|
||||
return (err, req, res, next) => {
|
||||
const host = new ExecutionContextHost([req, res]);
|
||||
try {
|
||||
Promise.resolve(targetCallback(err, req, res, next)).catch(e => {
|
||||
exceptionsHandler.next(e, res);
|
||||
exceptionsHandler.next(e, host);
|
||||
});
|
||||
} catch (e) {
|
||||
exceptionsHandler.next(e, res);
|
||||
exceptionsHandler.next(e, host);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class RpcContextCreator {
|
||||
const [data, ...params] = args;
|
||||
const canActivate = await this.guardsConsumer.tryActivate(
|
||||
guards,
|
||||
data,
|
||||
args,
|
||||
instance,
|
||||
callback,
|
||||
);
|
||||
@@ -65,7 +65,7 @@ export class RpcContextCreator {
|
||||
|
||||
return await this.interceptorsConsumer.intercept(
|
||||
interceptors,
|
||||
data,
|
||||
args,
|
||||
instance,
|
||||
callback,
|
||||
handler,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
|
||||
export class RpcProxy {
|
||||
public create(
|
||||
@@ -7,10 +8,11 @@ export class RpcProxy {
|
||||
exceptionsHandler: RpcExceptionsHandler,
|
||||
): (...args) => Promise<Observable<any>> {
|
||||
return async (...args) => {
|
||||
const host = new ExecutionContextHost(args);
|
||||
try {
|
||||
return await targetCallback(...args);
|
||||
} catch (e) {
|
||||
return exceptionsHandler.handle(e);
|
||||
return exceptionsHandler.handle(e, host);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@ import { Observable } from 'rxjs/Observable';
|
||||
import { RpcException } from './rpc-exception';
|
||||
import { RpcExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions';
|
||||
import { _throw } from 'rxjs/observable/throw';
|
||||
import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface';
|
||||
|
||||
export class RpcExceptionsHandler {
|
||||
private static readonly logger = new Logger(RpcExceptionsHandler.name);
|
||||
private filters: RpcExceptionFilterMetadata[] = [];
|
||||
|
||||
public handle(exception: Error | RpcException | any): Observable<any> {
|
||||
const filterResult$ = this.invokeCustomFilters(exception);
|
||||
public handle(
|
||||
exception: Error | RpcException | any,
|
||||
host: ArgumentsHost,
|
||||
): Observable<any> {
|
||||
const filterResult$ = this.invokeCustomFilters(exception, host);
|
||||
if (filterResult$) {
|
||||
return filterResult$;
|
||||
}
|
||||
@@ -41,7 +45,7 @@ export class RpcExceptionsHandler {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public invokeCustomFilters(exception): Observable<any> | null {
|
||||
public invokeCustomFilters(exception, host: ArgumentsHost): Observable<any> | null {
|
||||
if (isEmpty(this.filters)) return null;
|
||||
|
||||
const filter = this.filters.find(({ exceptionMetatypes, func }) => {
|
||||
@@ -52,6 +56,6 @@ export class RpcExceptionsHandler {
|
||||
);
|
||||
return hasMetatype;
|
||||
});
|
||||
return filter ? filter.func(exception) : null;
|
||||
return filter ? filter.func(exception, host) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,4 +56,8 @@ export class IoAdapter implements WebSocketAdapter {
|
||||
public bindMiddleware(server, middleware: (socket, next) => void) {
|
||||
server.use(middleware);
|
||||
}
|
||||
|
||||
public close(server) {
|
||||
isFunction(server.close) && server.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,4 +67,8 @@ export class WsAdapter implements WebSocketAdapter {
|
||||
const { callback } = messageHandler;
|
||||
return process(callback(message.data));
|
||||
}
|
||||
|
||||
public close(server) {
|
||||
isFunction(server.close) && server.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class WsContextCreator {
|
||||
return this.wsProxy.create(async (client, data) => {
|
||||
const canActivate = await this.guardsConsumer.tryActivate(
|
||||
guards,
|
||||
data,
|
||||
[client, data],
|
||||
instance,
|
||||
callback,
|
||||
);
|
||||
@@ -63,7 +63,7 @@ export class WsContextCreator {
|
||||
};
|
||||
return await this.interceptorsConsumer.intercept(
|
||||
interceptors,
|
||||
data,
|
||||
[client, data],
|
||||
instance,
|
||||
callback,
|
||||
handler,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { WsExceptionsHandler } from './../exceptions/ws-exceptions-handler';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
|
||||
export class WsProxy {
|
||||
public create(
|
||||
@@ -6,10 +7,11 @@ export class WsProxy {
|
||||
exceptionsHandler: WsExceptionsHandler,
|
||||
): (client, data) => Promise<void> {
|
||||
return async (client, data) => {
|
||||
const host = new ExecutionContextHost([client, data]);
|
||||
try {
|
||||
return await targetCallback(client, data);
|
||||
} catch (e) {
|
||||
exceptionsHandler.handle(e, client);
|
||||
exceptionsHandler.handle(e, host);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/ex
|
||||
import { isEmpty, isObject } from '@nestjs/common/utils/shared.utils';
|
||||
import { InvalidExceptionFilterException } from '@nestjs/core/errors/exceptions/invalid-exception-filter.exception';
|
||||
import { WsException } from '../exceptions/ws-exception';
|
||||
import { ArgumentsHost } from '@nestjs/common';
|
||||
|
||||
export class WsExceptionsHandler {
|
||||
private filters: ExceptionFilterMetadata[] = [];
|
||||
|
||||
public handle(exception: Error | WsException | any, client) {
|
||||
if (this.invokeCustomFilters(exception, client) || !client.emit) return;
|
||||
public handle(exception: Error | WsException | any, args: ArgumentsHost) {
|
||||
const client = args.switchToWs().getClient();
|
||||
if (this.invokeCustomFilters(exception, args) || !client.emit) return;
|
||||
|
||||
const status = 'error';
|
||||
if (!(exception instanceof WsException)) {
|
||||
@@ -33,7 +35,7 @@ export class WsExceptionsHandler {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public invokeCustomFilters(exception, client): boolean {
|
||||
public invokeCustomFilters(exception, args: ArgumentsHost): boolean {
|
||||
if (isEmpty(this.filters)) return false;
|
||||
|
||||
const filter = this.filters.find(({ exceptionMetatypes, func }) => {
|
||||
@@ -44,7 +46,7 @@ export class WsExceptionsHandler {
|
||||
);
|
||||
return hasMetatype;
|
||||
});
|
||||
filter && filter.func(exception, client);
|
||||
filter && filter.func(exception, args);
|
||||
return !!filter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@ import { InterceptorsContextCreator } from '@nestjs/core/interceptors/intercepto
|
||||
import { InterceptorsConsumer } from '@nestjs/core/interceptors/interceptors-consumer';
|
||||
|
||||
export class SocketModule {
|
||||
private socketsContainer = new SocketsContainer();
|
||||
private readonly socketsContainer = new SocketsContainer();
|
||||
private applicationConfig: ApplicationConfig;
|
||||
private webSocketsController: WebSocketsController;
|
||||
|
||||
public register(container, config) {
|
||||
this.applicationConfig = config;
|
||||
this.webSocketsController = new WebSocketsController(
|
||||
new SocketServerProvider(this.socketsContainer, config),
|
||||
container,
|
||||
@@ -65,9 +67,13 @@ export class SocketModule {
|
||||
);
|
||||
}
|
||||
|
||||
public close() {
|
||||
public async close() {
|
||||
if (!this.applicationConfig) {
|
||||
return void 0;
|
||||
}
|
||||
const adapter = this.applicationConfig.getIoAdapter();
|
||||
const servers = this.socketsContainer.getAllServers();
|
||||
servers.forEach(({ server }) => server && server.close && server.close());
|
||||
servers.forEach(({ server }) => server && adapter.close(server));
|
||||
this.socketsContainer.clear();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user