GraphQL 订阅、websocket、nodejs、Express 会话
Posted
技术标签:
【中文标题】GraphQL 订阅、websocket、nodejs、Express 会话【英文标题】:GraphQL subscription, websocket, nodejs, Express session 【发布时间】:2019-02-16 05:27:41 【问题描述】:我正在使用 Apollo Server 2(但这个问题不仅仅存在于 Apollo)和 Express.js vanilla(使用 apollo-server-express
)。
除了 Express session 机制之外,Subscriptions 也一切正常。
问题:
我正在使用 cookie-session(https://github.com/expressjs/cookie-session,但我认为对于 express-session 中间件也是如此)并且当我的浏览器启动新连接时对于我的服务器,ApolloServer onConnect
钩子没有 req
属性,也没有 req.session
等等......
我能做的是在webSocket.upgradeReq.headers.cookie
生命周期钩子中解析来自webSocket.upgradeReq.headers.cookie
的cookie,但在我看来这很hacky。
代码:
const ApolloServer = require('apollo-server-express')
const typeDefs = require('../src/graphql/types')
const resolvers = require('../src/graphql/resolvers')
const models = require('../src/models')
const apolloServer = new ApolloServer(
typeDefs,
resolvers,
context: ( req, connection ) =>
// connection exists only on webSocket connection
if (connection)
return
currentUser: connection.context.currentUser // <-- I NEED THIS!
// if not a (webSocket) connection it is a "default" HTTP call
return
models,
currentUser: id: req.user.id
,
subscriptions:
onConnect: (connectionParams, webSocket) =>
// "connectionParams" is from the client but I cannot use it because cookies are HTTP-Only
// I can retrieve cookies from here: "webSocket.upgradeReq.headers.cookie" but then I need to parse them which seems a bit hacky to me
// return currentUser: req.user.id // <-- I NEED THIS (req.user.id doesn't exists)!
)
module.exports = apolloServer
我在 Apollo Server Docs 网站上找不到任何内容(对于其他主题非常有据可查!)。
我哪里做错了?
【问题讨论】:
你是怎么解决的? 【参考方案1】:查看此评论:https://github.com/expressjs/cookie-session/issues/117#issuecomment-452046225 dougwilson 以获取会话值。
如果您只想获取会话内容(而不是写回新会话 - 无论如何没有 res 是不可能的),您可能只需要直接使用 cookies 模块,这是该模块在下面使用的引擎盖读取cookie并验证签名。一个可能的例子:
const Cookies = require('cookies') // … const cookies = new Cookies(req, null, keys ) const session = JSON.parse(Buffer.from(cookies.get('session'),'base64').toString('utf8'))
然后onConnect
返回您需要在 subscribe() 上使用的任何内容
【讨论】:
【参考方案2】:https://www.apollographql.com/docs/apollo-server/features/subscriptions.html#Authentication-Over-WebSocket
这似乎表明您可以在客户端传递所需的 cookie 数据,以便在服务器的上下文中接收。
传入 websocket 连接的参数:https://www.apollographql.com/docs/react/advanced/subscriptions.html#authentication
【讨论】:
【参考方案3】:我知道这个老问题,但我自己也遇到了这个问题,我如何解决它是我通过会话中间件运行 webSocket.upgradeReq.session。
然后在 onConnect 中:
你可以访问你的会话
【讨论】:
【参考方案4】:这是什么!使用
//Session middleware before resolvers
const sessionParser = session(
store,
secret: config.get('keys.session_secret'),
//...The rest config here
);
const apolloServer = new ApolloServer(
schema,
context: (req, res, connection: TheContext): TheContext =>
return req, res, redisStore: store, connection; //connection is must here
,
subscriptions:
onConnect: (_params, _ws, ctx) =>
return new Promise((resolve, reject) =>
const req = ctx.request as express.Request;
const res = ( as any) as express.Response;
//Parsing session
sessionParser(req, res, (_: any) =>
const session = req.session as any;
//Getting userId which was stored during login
const userId = session.userId;
if (!userId)
reject(new AppError('You are not logged in'));
//This will be available in subscription via connection as
//something.connection.context.userId
resolve(userId);
);
);
);
我像这样使用TypeGraphql
@Subscription(() => Post,
topics: (context) => `$Topic.DELETED_POST:$parseUserId(context)`,
description: 'To be used in requests list and not for individual request'
)
deletedPost(@Root() post: Post)
return post;
这就是我访问存储在会话中的 userId 的方式
export function parseUserId(obj: any): string
return obj.connection.context.userId ?? '';
感谢所有答案here
【讨论】:
以上是关于GraphQL 订阅、websocket、nodejs、Express 会话的主要内容,如果未能解决你的问题,请参考以下文章
graphql 订阅无法连接到弹性 beantalk 的 websocket 端点
GraphQL 订阅、websocket、nodejs、Express 会话
使用 WebSocket 的原始 javascript GraphQL 订阅不起作用