Ole*_*ich 10 typescript nestjs
我有一个全局拦截器,需要初始化我自己的请求上下文 DTO,并且我希望可以在处理当前请求的控制器中访问此 DTO。
到目前为止我找到的解决方案是创建 Request 范围内的可注入 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;
.....
}
}
Run Code Online (Sandbox Code Playgroud)
并将此类注入拦截器:
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
}));
}
}
Run Code Online (Sandbox Code Playgroud)
然后将这个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';
}
}
Run Code Online (Sandbox Code Playgroud)
有大量的解决方法可以实现这个简单的功能恕我直言此外,我有这篇文章描述了为什么使用基于范围的注入不好:https://guxi.me/posts/why-you-should-avoid-using-request -scope-injection-in-nest-js/
我的服务将会非常庞大,有大量的控制器和大量的可注入服务。根据这篇文章 - 我的服务在性能和内存使用方面将无法扩展。
我的问题是如何在 NestJS 中实现我需要的功能以及最佳实践是什么?另一个“额外问题” - RequestContext 类具有initialize接收快速请求并解析它的方法。我不喜欢它,我希望此类的每个属性都是只读的,并通过使用request对象调用构造函数以传统方式初始化此类...我如何通过@Inject策略实现它?
Jes*_*ter 15
如果您想在不使用请求范围提供程序的情况下执行此操作,您可以通过使用附加数据丰富请求对象来简化很多工作。从技术上讲,请求对象始终可用于入站 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();
}
Run Code Online (Sandbox Code Playgroud)
使用自定义装饰器可以轻松地在任何控制器中访问此值:
export const RequestContext = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.customRequestContext;
},
);
Run Code Online (Sandbox Code Playgroud)
然后在任何控制器中,您可以使用它来访问该值:
@Get()
async findOne(@RequestContext() requestContext: RequestContextInterface) {
// do whatever you need to do with it in your controllers
}
Run Code Online (Sandbox Code Playgroud)
共享请求上下文的一种方法是使用连续本地存储(CLS)包,例如express-http-context。那么你可以
// request-context.ts
import { createParamDecorator, HttpException, HttpStatus, Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import httpContext from 'express-http-context';
@Injectable()
export class RequestContextProvider {
get(key) {
return httpContext.get(key)
}
set(key, value) {
return httpContext.set(key, value)
}
}
@Injectable()
export class RequestContextMiddleware implements NestMiddleware {
constructor(private requestContextProvider: RequestContextProvider) { }
use(req: Request, res: Response, next: NextFunction) {
// first run express-http-context middleware
httpContext.middleware(req, res, () => {
// set context data
// for example extract user data from JWT
const [, token] = req.headers.authorization.split(' ')
const decoded: Record<string, unknown> = jwt_decode(token)
this.requestContextProvider.set('userId', decoded.userId)
next();
})
}
}
Run Code Online (Sandbox Code Playgroud)
应用程序中的Appy中间件
//app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { RequestContextMiddleware, RequestContextProvider } from './common/request-context';
import { MyModule } from './my/my.module';
@Module({
imports: [MyModule],
providers: [RequestContextProvider]
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(RequestContextMiddleware)
.forRoutes('*');
}
}
Run Code Online (Sandbox Code Playgroud)
像这样注入控制器
//my.controller.ts
import { Controller, Post } from '@nestjs/common';
@Controller('my')
export class MyController {
constructor(private requestContextProvider: RequestContextProvider) { }
@Post()
doSomething(): string {
const userId = this.requestContextProvider.get('userId')
// do something with user ID
//...
}
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用装饰器
// request-context.ts
export const UserId = createParamDecorator(() => {
const userId = httpContext.get('userId')
if (!userId) {
throw new HttpException(
'Missing authorisation credentials',
HttpStatus.FORBIDDEN
)
}
return userId;
})
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
21169 次 |
| 最近记录: |