【问题标题】:Next js and Apollo - Cookie not being passedNext js 和 Apollo - Cookie 未通过
【发布时间】:2019-10-09 08:14:50
【问题描述】:

我正在使用带有 Apollo 的 Next JS,并在我的 with-data HOC 中使用以下配置进行了设置:

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { withClientState } from 'apollo-link-state';
import { getMainDefinition } from 'apollo-utilities';
import { ApolloLink, Observable, split  } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import withApollo from 'next-with-apollo';
import { SubscriptionClient } from 'subscriptions-transport-ws';

import { endpoint, prodEndpoint, WSendpoint, WSprodEndpoint } from '../config';

import defaults from '../apollo-state/graphql/default-state';
import Mutation from '../apollo-state/resolvers/mutation-resolvers';

const wsClient = process.browser ? new SubscriptionClient(process.env.NODE_ENV === 'development' ? WSendpoint : WSprodEndpoint, {
  reconnect: true,
}) : null;


function createClient({ headers }) {
  const wsLink = process.browser ? new WebSocketLink(wsClient) : null;
  const httpLink = new HttpLink({
    uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
    credentials: 'include',
  })

  const link = process.browser ? split(
    // split based on operation type
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
  ) : httpLink;

  const cache = new InMemoryCache();

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers
    };
    operation.setContext(contextObj);
  }; 

  const requestLink = new ApolloLink((operation, forward) =>
    new Observable(observer => {
      let handle;
      Promise.resolve(operation)
        .then(oper => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer),
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
  );
  // end custom config functions
  const apolloClient = new ApolloClient({
      credentials: 'include',
      ssrMode: !process.browser,
      link: ApolloLink.from([
        onError(({ graphQLErrors, networkError }) => {
          if (graphQLErrors) {
            console.log(graphQLErrors)
          }
          if (networkError) {
            console.log(networkError)
          }
        }),
        requestLink,
        withClientState({
          defaults, // default state
          resolvers: {
            Mutation // mutations
          },
          cache
        }),
        link
      ]),
      cache
  }); // end new apollo client
  return apolloClient;
}

export { wsClient };
export default withApollo(createClient);

本地一切正常。我可以登录,当我访问该站点时它会自动登录,并且 SSR 工作没有问题。但是,当我部署到 Next 或 Heroku 时,SSR 不起作用。

我已经调查了这个问题,似乎这是一个常见的问题,即 cookie 没有随请求一起发送:

https://github.com/apollographql/apollo-client/issues/4455

https://github.com/apollographql/apollo-client/issues/4190

https://github.com/apollographql/apollo-client/issues/4193

问题似乎在于 Apollo 配置的这一部分,其中有时未定义标头,因此未发送 cookie:

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers
    };
    operation.setContext(contextObj);
  }; 

人们提到的一些解决方法是在存在标头时手动设置 cookie 标头:

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers: {
        cookie: headers && headers.cookie
      }
    };
    operation.setContext(contextObj);
  }; 

上述对代码的修改修复了服务器端渲染,但是当我在浏览器中使用登录的 cookie 访问该站点时,它将不再自动登录(它使用我的初始方法自动登录,但不会这样做生产中的 SSR)

人们提到它可以与 Now 或 Heroku 相关,使用部署应用程序后生成的 URL 中的子域并使用自定义域来解决问题。我尝试使用自定义域,但我仍然遇到问题。我的域设置是这样的:

前端域:mysite.com 后端域名:api.mysite.com

这里有没有人遇到过这个问题并能够解决它?

如果您发现我的配置有问题或我的域设置方式有问题,请告诉我。

【问题讨论】:

  • 如果您有其他解决方案,请与我们分享,真的很烦人。如果你没有其他的,@Ngatia Frankline 指出的解决方案对我来说真的很有效。
  • 我最终在 Next JS 服务器上设置了 cookie。这是我为这种方法复制的存储库:github.com/Alexloof/Next-GraphQL-Blog

标签: javascript reactjs graphql apollo next.js


【解决方案1】:

官方NextJs apollo example有问题或不足 这在issue 中报告了

我将把能够解决这个问题的 cmets 拼接起来

这是对官方示例的修改,由derozan10 发布,源自mzygmunt 的示例的前版帖子

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
import { endpoint } from '../config';

export default function createApolloClient(initialState, ctx) {
  // The `ctx` (NextPageContext) will only be present on the server.
  // use it to extract auth headers (ctx.req) or similar.

  const enchancedFetch = (url, init) =>
    fetch(url, {
      ...init,
      headers: {
        ...init.headers,
        Cookie: ctx.req.headers.cookie,
      },
    }).then(response => response);

  return new ApolloClient({
    ssrMode: Boolean(ctx),
    link: new HttpLink({
      uri: endpoint, // Server URL (must be absolute)
      credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
      fetch: ctx ? enchancedFetch : fetch,
    }),
    cache: new InMemoryCache().restore(initialState),
  });
}

为了让它工作,我还在后端更改了我的 CORS 选项


// a graphql-yoga + prisma 1 backend (Wes Boss' Advanced React class)
...
const cors = require("cors");

const server = createServer();

var corsOptions = {
  origin: process.env.FRONTEND_URL,
  credentials: true
};
server.express.use(cors(corsOptions));
...

我还更新了依赖项,直到我可以达到“无纱线警告”状态

"dependencies": {
    "@apollo/react-hooks": "^3.1.5",
    "@apollo/react-ssr": "^3.1.5",
    "@babel/core": "^7.1.2",
    "@types/react": "^16.8.0",
    "apollo-cache-inmemory": "^1.6.6",
    "apollo-client": "^2.6.9",
    "apollo-link-http": "^1.5.17",
    "apollo-utilities": "^1.3.2",
    "graphql": "14.3.1",
    "graphql-tag": "^2.10.3",
    "isomorphic-unfetch": "^3.0.0",
    "next": "latest",
    "prop-types": "^15.6.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1"
  },
  "devDependencies": {
    "babel-plugin-graphql-tag": "^2.5.0"
  }

【讨论】:

    【解决方案2】:

    晚了,但试试这个

    您应该添加 cookie 选项,如下所示。请确保您的浏览器中有一个 cookie,即 csrftoken。它应该可用。希望它有效。

    常量请求 = 异步操作 => { 常量 contextObj = { 获取选项:{ 凭据:“包括” }, //################ 在这里更改########## 标题:{ ...标题 } 饼干: { ...饼干 } //###################################### }; operation.setContext(contextObj); };

    【讨论】:

      猜你喜欢
      • 2020-08-13
      • 2020-08-28
      • 2021-07-31
      • 2022-11-10
      • 2020-09-29
      • 2021-09-21
      • 2023-02-22
      • 2021-10-10
      • 1970-01-01
      相关资源
      最近更新 更多