NestJS上下文在graphql订阅中未定义

NestJS上下文在graphql订阅中未定义,graphql,nestjs,Graphql,Nestjs,有人能帮我吗,为什么我的订阅中没有定义上下文 @Subscription(returns => CommentsDto, { filter: (payload, variables, context) => { console.log({ payload, variables, context }) // <------------ context context undefined const isSameCode = variabl

有人能帮我吗,为什么我的订阅中没有定义上下文

@Subscription(returns => CommentsDto, {
    filter: (payload, variables, context) => {
        console.log({ payload, variables, context })  // <------------ context context undefined
        const isSameCode = variables.code === payload.newComment.code
        const isAuthorized = context.req.headers.clientauthorization === payload.clientauthorization
        return isSameCode && isAuthorized
    },
})
newComment(
    @Context() context,  
    @Args(({ name: 'code', type: () => String })) code: string,
) {
    console.log(context) // <------------ undefined 
    return this.publisherService.asyncIterator('newComment')
}

感谢您的帮助,因为在订阅的情况下Req和Res是未定义的,所以当您尝试记录上下文时,它是未定义的

要使上下文可用,您需要更改用于返回上下文(可在连接变量中找到)的保护。 总而言之:

  • =>http/query和http中使用的req、res
  • =>WebSocket/订阅中使用的连接
现在,要正确获取上下文,您必须严格执行以下步骤:

  • 修改应用程序模块文件以使用GraphqlModuleImport
  • 修改Extract User Guard和Auth Guard(或您正在使用的任何防护) 返回查询/变异和订阅案例的数据
  • 使用订阅中的上下文接收数据
  • 在身份验证服务中添加jwtTokenPayload提取器函数
  • Opitonal:Typescript的助手函数和DTO
  • 1-详情:

     GraphQLModule.forRootAsync({
          //import AuthModule for JWT headers at graphql subscriptions
          imports: [AuthModule],
          //inject Auth Service
          inject: [AuthService],
          useFactory: async (authService: AuthService) => ({
            debug: true,
            playground: true,
            installSubscriptionHandlers: true,
            // pass the original req and res object into the graphql context,
            // get context with decorator `@Context() { req, res, payload, connection }: GqlContext`
            // req, res used in http/query&mutations, connection used in webSockets/subscriptions
            context: ({ req, res, payload, connection }: GqlContext) => ({
              req,
              res,
              payload,
              connection,
            }),
            // subscriptions/webSockets authentication
            typePaths: ["./**/*.graphql"],
            resolvers: { ...resolvers },
            subscriptions: {
              // get headers
              onConnect: (connectionParams: ConnectionParams) => {
                // convert header keys to lowercase
                const connectionParamsLowerKeys: Object = mapKeysToLowerCase(
                  connectionParams,
                );
                // get authToken from authorization header
                let authToken: string | false = false;
    
                const val = connectionParamsLowerKeys["authorization"];
    
                if (val != null && typeof val === "string") {
                  authToken = val.split(" ")[1];
                }
    
                if (authToken) {
                  // verify authToken/getJwtPayLoad
                  const jwtPayload: JwtPayload = authService.getJwtPayLoad(
                    authToken,
                  );
    
                  // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
                  return {
                    currentUser: jwtPayload.username,
                    jwtPayload,
                    headers: connectionParamsLowerKeys,
                  };
                }
                throw new AuthenticationError("authToken must be provided");
              },
            },
            definitions: {
              path: join(process.cwd(), "src/graphql.classes.ts"),
              outputAs: "class",
            },
          }),
        }),
    
    2-详细信息: 我的getRequest函数示例来自扩展AuthGuard(jwt)类的ExtractUserGuard类

    更改为:

      getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
        const request = ctx.getContext().req;
        return request;}
    
    为此:

    
     getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
    // req used in http queries and mutations, connection is used in websocket subscription connections, check AppModule
        const { req, connection } = ctx.getContext();
        // if subscriptions/webSockets, let it pass headers from connection.context to passport-jwt
        const requestData =
          connection && connection.context && connection.context.headers
            ? connection.context
            : req;
        return requestData;
    }
    
    3-现在您可以在解析器中获取此数据

      @Subscription("testSubscription")
      @UseGuards(ExtractUserGuard)
      async testSubscription(
        @Context("connection") connection: any,
      ): Promise<JSONObject> {
        const subTopic = `${Subscriptions_Test_Event}.${connection.context.jwtPayload.email}`;
        console.log("Listening to the event:", subTopic);
    
        return this.pubSub.asyncIterator(subTopic);
      }
    
    5-Helper函数和DTO示例(我在项目中使用的)

    DTO:

    辅助功能:

    export function mapKeysToLowerCase(
      inputObject: Record<string, any>,
    ): Record<string, any> {
      let key;
      const keys = Object.keys(inputObject);
      let n = keys.length;
      const newobj: Record<string, any> = {};
      while (n--) {
        key = keys[n];
        newobj[key.toLowerCase()] = inputObject[key];
      }
      return newobj;
    }
    
    导出函数MapKeyStore小写(
    输入对象:记录,
    ):记录{
    让钥匙;
    常量键=Object.keys(inputObject);
    设n=keys.length;
    const newobj:Record={};
    而(n--){
    键=键[n];
    newobj[key.toLowerCase()]=inputObject[key];
    }
    返回newobj;
    }
    
    因为在订阅的情况下,Req和Res是未定义的,所以当您尝试记录上下文时,它是未定义的

    要使上下文可用,您需要更改用于返回上下文(可在连接变量中找到)的保护。 总而言之:

    • =>http/query和http中使用的req、res
    • =>WebSocket/订阅中使用的连接
    现在,要正确获取上下文,您必须严格执行以下步骤:

  • 修改应用程序模块文件以使用GraphqlModuleImport
  • 修改Extract User Guard和Auth Guard(或您正在使用的任何防护) 返回查询/变异和订阅案例的数据
  • 使用订阅中的上下文接收数据
  • 在身份验证服务中添加jwtTokenPayload提取器函数
  • Opitonal:Typescript的助手函数和DTO
  • 1-详情:

     GraphQLModule.forRootAsync({
          //import AuthModule for JWT headers at graphql subscriptions
          imports: [AuthModule],
          //inject Auth Service
          inject: [AuthService],
          useFactory: async (authService: AuthService) => ({
            debug: true,
            playground: true,
            installSubscriptionHandlers: true,
            // pass the original req and res object into the graphql context,
            // get context with decorator `@Context() { req, res, payload, connection }: GqlContext`
            // req, res used in http/query&mutations, connection used in webSockets/subscriptions
            context: ({ req, res, payload, connection }: GqlContext) => ({
              req,
              res,
              payload,
              connection,
            }),
            // subscriptions/webSockets authentication
            typePaths: ["./**/*.graphql"],
            resolvers: { ...resolvers },
            subscriptions: {
              // get headers
              onConnect: (connectionParams: ConnectionParams) => {
                // convert header keys to lowercase
                const connectionParamsLowerKeys: Object = mapKeysToLowerCase(
                  connectionParams,
                );
                // get authToken from authorization header
                let authToken: string | false = false;
    
                const val = connectionParamsLowerKeys["authorization"];
    
                if (val != null && typeof val === "string") {
                  authToken = val.split(" ")[1];
                }
    
                if (authToken) {
                  // verify authToken/getJwtPayLoad
                  const jwtPayload: JwtPayload = authService.getJwtPayLoad(
                    authToken,
                  );
    
                  // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
                  return {
                    currentUser: jwtPayload.username,
                    jwtPayload,
                    headers: connectionParamsLowerKeys,
                  };
                }
                throw new AuthenticationError("authToken must be provided");
              },
            },
            definitions: {
              path: join(process.cwd(), "src/graphql.classes.ts"),
              outputAs: "class",
            },
          }),
        }),
    
    2-详细信息: 我的getRequest函数示例来自扩展AuthGuard(jwt)类的ExtractUserGuard类

    更改为:

      getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
        const request = ctx.getContext().req;
        return request;}
    
    为此:

    
     getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
    // req used in http queries and mutations, connection is used in websocket subscription connections, check AppModule
        const { req, connection } = ctx.getContext();
        // if subscriptions/webSockets, let it pass headers from connection.context to passport-jwt
        const requestData =
          connection && connection.context && connection.context.headers
            ? connection.context
            : req;
        return requestData;
    }
    
    3-现在您可以在解析器中获取此数据

      @Subscription("testSubscription")
      @UseGuards(ExtractUserGuard)
      async testSubscription(
        @Context("connection") connection: any,
      ): Promise<JSONObject> {
        const subTopic = `${Subscriptions_Test_Event}.${connection.context.jwtPayload.email}`;
        console.log("Listening to the event:", subTopic);
    
        return this.pubSub.asyncIterator(subTopic);
      }
    
    5-Helper函数和DTO示例(我在项目中使用的)

    DTO:

    辅助功能:

    export function mapKeysToLowerCase(
      inputObject: Record<string, any>,
    ): Record<string, any> {
      let key;
      const keys = Object.keys(inputObject);
      let n = keys.length;
      const newobj: Record<string, any> = {};
      while (n--) {
        key = keys[n];
        newobj[key.toLowerCase()] = inputObject[key];
      }
      return newobj;
    }
    
    导出函数MapKeyStore小写(
    输入对象:记录,
    ):记录{
    让钥匙;
    常量键=Object.keys(inputObject);
    设n=keys.length;
    const newobj:Record={};
    而(n--){
    键=键[n];
    newobj[key.toLowerCase()]=inputObject[key];
    }
    返回newobj;
    }
    
    这方面运气好吗?这方面运气好吗?