我有许多服务都需要知道请求中的租户 ID(保存在 JWT 身份验证 token 中)。请求是 GRPC(jwt 存储在 MetaData 中)或 Graphql(jwt 存储在 context.headers.authorization 中)。
我希望能够强制自己在使用服务时不要忘记传递此租户 ID。理想情况下,我什至不想不断编写相同的代码来从请求中获取信息并将其传递。然而,我设法做到这一点的唯一方法是使用:
@Inject(REQUEST)
用于服务构造函数中的 grpc。这不适用于 graphql 请求。我看到的唯一其他方法是仅在提供数据后返回服务方法,这看起来很难看:
class MyService {
private _actions: {
myMethod1() { ... }
}
withTenantDetails(details) {
this._details = details;
return this._actions;
}
}
如果我能以某种方式获取 MyService 中的执行上下文,那将是一个不错的选择,并且可以轻松使用:
const getTenantId = (context: ExecutionContext) => {
if (context.getType() === 'rpc') {
logger.debug('received rpc request');
const request = context.switchToRpc().getContext();
const token = request.context.get("x-authorization");
return {
token,
id: parseTokenTenantInfo(token)
};
}
else if (context.getType<GqlContextType>() === 'graphql') {
logger.debug('received graphql request');
const gqlContext = GqlExecutionContext.create(context);
const request = gqlContext.getContext().request;
const token = request.get('Authorization');
return {
token,
id: parseTokenTenantInfo(token)
};
}
else {
throw new Error(`Unknown context type receiving in tenant param decorator`)
}
}
但是我找不到任何方法将执行上下文传递到服务,而不必每次都记住传递它。
最佳答案
可以将Request
注入(inject)到可注入(inject)服务中。
为此,Service
将是 Scope.Request
,而不再是 Singleton
,因此将为每个请求创建一个新实例。这是一个重要的考虑因素,以避免出于性能原因创建太多资源。
可以使用以下方式明确此范围:
@Injectable({ scope: Scope.REQUEST })
app.service.ts:
@Injectable({ scope: Scope.REQUEST })
export class AppService {
tenantId: string;
constructor(@Inject(REQUEST) private request: Request) {
// because of @Inject(REQUEST),
// this service becomes REQUEST SCOPED
// and no more SINGLETON
// so this will be executed for each request
this.tenantId = getTenantIdFromRequest(this.request);
}
getData(): Data {
// some logic here
return {
tenantId: this.tenantId,
//...
};
}
}
// this is for example...
const getTenantIdFromRequest = (request: Request): string => {
return request?.header('tenant_id');
};
请注意,另一种方法可能是一次性解码 JWT,而不是解码 JWT token 以检索每个请求以及其他服务(每个服务一个)的 TENANT_ID
,然后将其添加到 Request
对象中。
可以用 global Guard 来完成,与官方文档的授权 guard 示例相同。
这里只是一个简单的例子:(可以与 Auth Guard 合并)
@Injectable()
export class TenantIdGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
request['tenantId'] = getTenantIdFromRequest(request);
return true; // or any other validation
}
}
对于 GraphQL
应用程序,我们应该注入(inject) CONTEXT
来代替 REQUEST
:
constructor(@Inject(CONTEXT) private context) {}
您必须在上下文内设置请求,或直接在上下文内设置TENANT_ID
,以便在内部服务后检索它。
关于nestjs - 在nestjs中将请求数据传递给服务的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72788267/