nestjs - 在nestjs中将请求数据传递给服务的正确方法是什么?

标签 nestjs

我有许多服务都需要知道请求中的租户 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/

相关文章:

node.js - 如何将 nartc/automapper 中的配置文件使用到 Nestjs 应用程序中

nestjs - 使用 typeorm 和 mysql 的 Nest 框架,列默认属性正在将数据库值修改为每次服务器重新加载时的默认值

jestjs - 我可以在 NestJS 中的 .spec.ts 之外使用 Jest 的 Expect() 吗?

graphql - Nestjs GraphQL 订阅 onConnect 和 onDisconnect 回调

nestjs 与普通 express 性能

node.js - 在 sequelize getter 和 setter 函数中使用时出现 "Argument of type is not assignable to parameter of type"错误

event-handling - 无法捕获 NestJs 中的事件

node.js - Nest.js 测试错误 : Using the "extends Logger" instruction is not allowed in Nest v8. 请改用 "extends ConsoleLogger"

nestjs - 你如何在 NestJs 中使用回退异常过滤器

javascript - 将 Express 应用程序迁移到 NestJS