【问题标题】:GraphQL subscription, websocket, nodejs, Express sessionGraphQL 订阅、websocket、nodejs、Express 会话
【发布时间】:2019-02-16 05:27:41
【问题描述】:

我正在使用 Apollo Server 2(但这个问题不仅仅存在于 Apollo)和 Express.js vanilla(使用 apollo-server-express)。

除了 Express session 机制之外,Subscriptions 也一切正常。

问题:

我正在使用 cookie-sessionhttps://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 网站上找不到任何内容(对于其他主题非常有据可查!)。

我哪里做错了?

【问题讨论】:

  • 你是怎么解决的?

标签: express websocket graphql session-cookies apollo-server


【解决方案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

        【讨论】:

          猜你喜欢
          • 2020-11-17
          • 2023-04-03
          • 2017-09-01
          • 2022-08-07
          • 2022-09-29
          • 2018-08-24
          • 2022-10-12
          • 2013-09-09
          • 2019-08-01
          相关资源
          最近更新 更多