【问题标题】:Why am I getting 500 http error when making serverside apollo client queries with cors?为什么在使用 cors 进行服务器端 apollo 客户端查询时出现 500 http 错误?
【发布时间】:2020-06-03 19:03:49
【问题描述】:

我正在为我的网站使用 Next.js + react-apollo + apollo-server + express。最近我添加了 cookie 身份验证,因此必须在服务器中启用 CORS 才能使 cookie 身份验证工作。但是我看到阿波罗客户端查询在服务器端执行时会导致 http 500 状态。执行客户端成功解析时相同的查询。我很困惑,因为我实际上预计问题会发生在客户端,因为 CORS 在那里有更大的影响。我不确定是什么导致了问题,欢迎提出任何建议!

错误本身如下:

ApolloError:网络错误:请求https://example.com/graphql/ 失败,原因:写 EPROTO 140152723232576:error:14094410:SSL 例程:ssl3_read_bytes:sslv3 警报握手 失败:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1544:SSL 警报编号 40

    at new ApolloError (/src/node_modules/apollo-client/bundle.umd.js:92:26)
    at /src/node_modules/apollo-client/bundle.umd.js:1588:34
    at /src/node_modules/apollo-client/bundle.umd.js:2008:15
    at Set.forEach (<anonymous>)
    at /src/node_modules/apollo-client/bundle.umd.js:2006:26
    at Map.forEach (<anonymous>)
    at QueryManager.broadcastQueries (/src/node_modules/apollo-client/bundle.umd.js:2004:20)
    at /src/node_modules/apollo-client/bundle.umd.js:1483:29
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  graphQLErrors: [],

networkError: FetchError: 请求https://example.com/graphql/ 失败,原因:写 EPROTO 140152723232576:error:14094410:SSL 例程:ssl3_read_bytes:sslv3 警报握手 失败:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1544:SSL 警报编号 40

我使用的是 Amazon Cloudfront 提供的 SSL 证书。

这是我的客户端代码:

_app.js:

class MyApp extends App {
  static async getInitialProps({ Component, ctx }) {
    let pageProps = {};
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
      if (pageProps.errorStatusCode && ctx.res) {
        ctx.res.statusCode = pageProps.errorStatusCode;
      }
    }

    return { pageProps };
  }
  render() { //render... }
}

我有一个用于页面查询的 HOC:

const withQuery = (Page, query, variables, errorPolicy = 'none') => {
  Page.getInitialProps = async ctx => {
    const { apolloClient } = ctx;
    try {
      const { data } = await apolloClient.query({
        query,
        variables: vars,
        errorPolicy
      });
      return { data };
    } catch (error) {
      return { errorStatusCode: error.networkError ? '500' : '404' };
    }
  };
  // if (typeof window === 'undefined') { // THIS CODE IS CAUSING THE ISSUE
  //   return Page;
  // }
}

这是我启动 apollo 客户端的方式:

import withApollo from 'next-with-apollo';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import introspectionQueryResultData from '../../fragmentTypes.json';

const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData
});

function createClient({ ctx, headers, initialState }) {
  return new ApolloClient({
    credentials: 'include',
    uri: 'some_graphql_url',
    cache: new InMemoryCache({ fragmentMatcher }).restore(initialState || {}),
    headers
  });
}

export default withApollo(createClient, { getDataFromTree: 'ssr' });

这是我的服务器代码:

import cors from 'cors'
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const { schema } = require('./models')
const server = new ApolloServer({
  schema,
})

// required settings to accept cookies
const corsOptions = {
  origin: function (origin, callback) {
    if (corsWhitelist.indexOf(origin) !== -1 || !origin) {
      callback(null, true)
    } else {
      callback(new Error(`${origin} - not allowed by CORS`))
    }
  },
  credentials: true
}

let app = express()
app.use(cors(corsOptions))
server.applyMiddleware({ app, cors: false })
const serverUrl = `my_server_url`
app.listen({ port }, () => console.log(`????  Server ready at ${serverUrl}`))

总结一下我的发现:

  1. 问题源于_app.js 调用await Component.getInitialProps(ctx)
  2. getInitialProps 定义在withQuery HOC 中,查询由apolloClient.query 方法执行。

没有 CORS 一切正常。

编辑:我注意到当 headers 选项与 CORS 一起添加到 createClient 时,问题将开始发生。

EDIT2:即使没有CORS也会发生错误,将headers选项添加到创建apollo客户端的createClient就足够了。

【问题讨论】:

  • 我所知道的开始解决500 Internal Server Error 的唯一方法是查看服务器端日志文件。
  • @EternalHour 不,它没有。我在网站上从来没有遇到过 SSL 问题,所以它们只是在我添加 CORS 时才开始的。
  • 问题中引用的错误消息似乎非常清楚地表明您在 SSL 错误中遇到的实际问题。我认为这里的任何其他人都无法猜到为什么在“添加 CORS”之后您可能会开始遇到 SSL 问题,但这仍然是 SSL 问题,而不是 CORS 问题。

标签: javascript ssl next.js apollo-client


【解决方案1】:

我收到错误的原因是因为我将所有标头从服务器端 (express.js) 传递给 Amazon Cloudfront,包括 host 标头。因为我的网站配置为使用SNI,所以传递正确的服务器名很重要(此参数用于确定从 IP 地址为哪个虚拟主机提供 SSL 证书)。碰巧 Apollo 客户端使用 node-fetch npm 包来发出 http 请求,而后者又使用 https Node.js 模块。如果host 标头存在,则https 将服务器名设置为host 的值,否则服务器名获取主机名(mywebsite.com)的值。所以在我的例子中,服务器名称是bla.bla.elasticbeanstalk.com,这当然会导致 SSL 握手错误,因为 SSL 证书是针对 mywebsite.com 的。我写了更多信息here

【讨论】:

  • 从 vercel 构建时遇到相同的错误(使用 getStaticProps 获取数据,即 SSG)。你知道这个修复如何在那里应用吗?
  • 问题发生在我的代码中,因为我在初始化ApolloClient 时覆盖了headers 对象。它与构建无关。你应该检查你的标题,也许阅读我写的链接,它解释了有关 SNI 错误的更多信息。
  • 我确实看过,但不要认为我可以将 getStaticProps 中的标头传递给我的 apolloClient。由于 getStaticProps 在构建时运行,因此它不会接收仅在请求期间可用的数据,例如查询参数或 HTTP 标头,因为它会生成静态 HTML。 (nextjs.org/docs/basic-features/data-fetching)
【解决方案2】:

您的代码不完整


const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = express();
server.applyMiddleware({ app });

app.listen({ port: 4000 }, () =>
  console.log(`? Server ready at http://localhost:4000${server.graphqlPath}`)
);

【讨论】:

  • 我在包含 typeDefs 和解析器的文件中导入 schema 对象。
猜你喜欢
  • 2021-01-08
  • 1970-01-01
  • 2019-10-18
  • 1970-01-01
  • 1970-01-01
  • 2020-09-26
  • 2018-05-18
  • 2016-08-31
  • 2021-06-01
相关资源
最近更新 更多