typescript - NestJS 初始化和传递请求上下文的最佳实践是什么
问题描述
我有一个全局拦截器,它需要初始化我自己的请求上下文 DTO,我希望这个 DTO 可以在处理当前请求的控制器中访问。
到目前为止我找到的解决方案是创建请求范围的可注入 RequestContext 类:
import {
Injectable,
Scope
} from '@nestjs/common';
import { Request } from 'express';
import { IncomingHttpHeaders } from 'http';
@Injectable({ scope: Scope.REQUEST })
export class RequestContext {
public headers: IncomingHttpHeaders;
....
initialize(request: Request) {
this.headers = request.headers;
.....
}
}
并将此类注入拦截器:
import {
NestInterceptor,
ExecutionContext,
CallHandler,
Injectable,
Inject
} from '@nestjs/common';
import { Request } from 'express';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { RequestContext } from '../dto';
@Injectable()
export class RequestContextInterceptor implements NestInterceptor {
constructor(
@Inject(RequestContext)
protected requestContext: RequestContext
) { }
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest<Request>();
this.requestContext.initialize(request);
return next.handle()
.pipe(
tap(() => {
// decorate response
}));
}
}
然后在每个控制器中注入这个 RequestContext ......
import {
Controller,
UseInterceptors,
Inject,
Get
} from '@nestjs/common';
import { BaseMicroserviceController } from '../core/base/base-microservice.controller';
import { RequestContext } from '../dto';
import { DispatchService } from '../services';
@Controller('api/v1/example')
export class ExampleController extends BaseMicroserviceController {
constructor (
@Inject(RequestContext)
protected requestContext: RequestContext,
protected dispatcheService: DispatchService
) {
super(dispatcheService);
}
@Get()
test() {
return 'test';
}
}
有巨大的解决方法来实现这个简单的功能恕我直言此外,我有这篇文章描述了为什么使用基于范围的注入不好:https ://guxi.me/posts/why-you-should-avoid-using-request -scope-injection-in-nest-js/
我的服务将是巨大的,有大量的控制器和大量的可注入服务。根据这篇文章 - 我的服务在性能和内存使用方面将不可扩展。
我的问题是如何在 NestJS 中实现我需要的功能,最佳实践是什么?另一个“奖励问题” - RequestContext 类具有initialize
接收快速请求并对其进行解析的方法。我不喜欢它,我希望这个类的每个属性都是只读的,并通过使用request
对象调用构造函数以传统方式初始化这个类......我如何用@Inject
策略来实现它?
解决方案
如果您想在不使用 Request Scoped 提供程序的情况下执行此操作,您可以通过使用附加数据丰富请求对象来简化很多工作。无论您使用什么注入范围,请求对象在技术上始终可用于入站 HTTP 交互。您可以RequestContext
完全放弃,只需将您想要的任何其他数据添加到拦截器内的请求对象。
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest<Request>();
const customRequestContext = initialize(request); // whatever you need to do to build this
request.customRequestContext = customRequestContext;
return next.handle();
}
使用自定义装饰器很容易在任何控制器中访问此值:
export const RequestContext = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.customRequestContext;
},
);
然后在您的任何控制器中,您都可以使用它来访问该值:
@Get()
async findOne(@RequestContext() requestContext: RequestContextInterface) {
// do whatever you need to do with it in your controllers
}
推荐阅读
- c# - 如何将列表存储到数组中,然后通过视图包返回
- ios - App Store 收据中缺少原始应用程序版本
- html - 在jade/pug 中阻止HTML 的整个部分,而不是单独的行
- html - 如何在css中的div边框中间创建一条曲线?
- sql - 如何在 SQL Server 中计算余额?
- python - 用 python 修补 module.object
- python - Python缩进字符串到JSON
- reactjs - Service Worker 注册期间出错:DOMException: Failed to register a ServiceWorker for scope.An SSL certificate error occurred
- getstream-io - 移动后端或前端与 GetStream 的集成
- java - 如何以最佳方式使用 toString 方法与 StringBuilder