gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-05 02:07:32 35 4
gpt4 key购买 nike

有人可以帮助我吗,为什么我的订阅中的 CONTEXT 未定义?

@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')
}

它正在为查询和 Mutatinos 工作......

Graphql 定义是:

const GraphQLDefinition = GraphQLModule.forRoot({
context: ({ req, connection }) => {
// subscriptions
if (connection) {
return { req: connection.context }
}

// queries and mutations
return { req }
},

installSubscriptionHandlers: true,
path: '/graphql',
playground: true,

})

谢谢你的帮助

最佳答案

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

为了使上下文可用,您需要更改正在使用的守卫以返回可以在连接变量中找到的上下文。基本上总结一下:

  • => req, res 用于http/query & mutations
  • => webSockets/订阅中使用的连接

现在要正确获取上下文,您必须准确执行以下步骤:

  1. 修改 App 模块文件以使用 GraphqlModuleImport
  2. 修改 Extract User Guard 和 Auth guard(或者你正在使用的任何防护)返回查询/变异和订阅案例的数据。
  3. 使用订阅中的上下文接收数据。
  4. 在 Auth 服务中添加 jwtTokenPayload 提取器功能。
  5. 可选: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);
}

4- 要使用 token 获取 jwtPayload,请将以下函数添加到您的 AuthService。

  getJwtPayLoad(token: string): JwtPayload {
const jwtPayload = this.jwtService.decode(token);
return jwtPayload as JwtPayload;
}

5-Helper Functions 和 DTO 示例(我在我的项目中使用过)

DTO:

export interface JwtPayload {
username?: string;
expiration?: Date;
}
export interface GqlContext {
req: Request;
res: Response;
payload?: JwtPayload;
// required for subscription
connection: any;
}
export interface ConnectionParams {
authorization: string;
}

辅助函数:

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;
}

关于graphql - NestJS 上下文在 graphql 订阅中未定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61710825/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com