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/订阅中使用的连接
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/订阅中使用的连接
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;
}
这方面运气好吗?这方面运气好吗?