使用 Apollo 进行服务器端渲染:getaddrinfo ENOTFOUND

Server side rendering with Apollo: getaddrinfo ENOTFOUND

我 运行 Apollo/React 使用 Express,我正在尝试让服务器端渲染正常工作。 Apollo docs 建议使用以下初始化代码连接到 API 服务器:

app.use((req, res) => {
  match({ routes, location: req.originalUrl }, (error, redirectLocation, renderProps) => {
    const client = new ApolloClient({
      ssrMode: true,
      networkInterface: createNetworkInterface({
        uri: 'http://localhost:3000', // Instead of 3010
        opts: {
          credentials: 'same-origin',
          headers: {
            cookie: req.header('Cookie'),
          },
        },
      }),
    });

    const app = (
      <ApolloProvider client={client}>
        <RouterContext {...renderProps} />
      </ApolloProvider>
    );

    getDataFromTree(app).then(() => {
      const content = ReactDOM.renderToString(app);
      const initialState = {[client.reduxRootKey]: client.getInitialState()  };
      const html = <Html content={content} state={initialState} />;
      res.status(200);
      res.send(`<!doctype html>\n${ReactDOM.renderToStaticMarkup(html)}`);
      res.end();
    });

  });
});

它使用 match() function from React Router v3 (as evidenced by package.json in the "GitHunt" example linked from the docs). I'm using React Router v4 from which match() is absent, so I refactored the code as follows, using renderRoutes() from the react-router-config package.

app.use((req, res) => {
  const client = new ApolloClient(/* Same as above */)

  const context = {}

  const app = (
    <ApolloProvider client={client}>
      <StaticRouter context={context} location={req.originalUrl}>
        { renderRoutes(routes) }
      </StaticRouter>
    </ApolloProvider>
  )

  getDataFromTree(app).then(/* Same as above */)
})

我的理解是 <StaticRouter> 避免了 match() 的使用。但是 react-router-config 提供了一个 matchRoutes() 函数,如果需要,它似乎提供了类似的功能(尽管没有回调)。

当我访问http://localhost:3000, the page loads as expected and I can follow links to subdirectories (e.g. http://localhost:3000/folder)。 当我尝试通过在地址栏中键入名称来直接加载子目录时,我的浏览器一直在等待服务器响应。大约六秒后,终端显示以下错误之一(不确定是什么导致错误在后续尝试中发生变化):

(node:1938) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Network error: request to http://localhost:3000 failed, reason: getaddrinfo ENOTFOUND localhost localhost:3000

(node:8691) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Network error: request to http://localhost:3000 failed, reason: socket hang up

我已经为这个问题苦苦挣扎了几个小时,但似乎无法弄清楚。 similar problem 的解决方案似乎与这种情况无关。任何帮助将不胜感激!

更多信息

如果我不关闭 nodemon 服务器,一段时间后我会收到数千个以下错误:

POST / - - ms - -

(node:1938) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 4443): Error: Network error: request to http://localhost:3000 failed, reason: socket hang up

但是,如果我确实终止了服务器,我会立即收到此错误:

/Users/.../node_modules/duplexer/index.js:31

writer.on("drain", function() {

      ^

TypeError: Cannot read property 'on' of undefined

at duplex (/Users/.../node_modules/duplexer/index.js:31:11)

at Object.module.exports (/Users/.../node_modules/stream-combiner/index.js:8:17)

at childrenOfPid (/Users/.../node_modules/ps-tree/index.js:50:6)

at kill (/Users/.../node_modules/nodemon/lib/monitor/run.js:271:7)

at Bus.onQuit (/Users/.../node_modules/nodemon/lib/monitor/run.js:327:5)

at emitNone (events.js:91:20)

at Bus.emit (events.js:188:7)

at process. (/Users/.../node_modules/nodemon/lib/monitor/run.js:349:9)

at Object.onceWrapper (events.js:293:19)

at emitNone (events.js:86:13)

此外,端口 3000 是正确的。如果我更改数字,则会出现不同的错误:

(node:2056) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Network error: request to http://localhost:3010 failed, reason: connect ECONNREFUSED 127.0.0.1:3010

您运行您的服务器/项目在容器中吗?我遇到了与此相同的问题,并最终执行了以下操作来解决它。

const networkInterface = createNetworkInterface({ uri: process.browser ? 'http://0.0.0.0:1111/graphql' : 'http://my-docker-container-name:8080/graphql' })

我在 docker-compose.yml 中为我的容器创建了一个内部 docker 网络,它允许容器相互通信,但是浏览器与 GQL 通信服务器在另一个 URL 上,导致您在 SSR getaddrinfo ENOTFOUND 上描述的问题,因此尽管它在客户端工作,但在 SSR 上它会失败。

我正在使用 nextJS 框架,它能够检测浏览器或 SSR,我相信你可以在 nextJS 之外做同样的事情。

我终于发现错误是由于 renderToStringrenderToStaticMarkup 无法通过导入 ReactDOM 获得。

导入语句 import ReactDOM from 'react-dom' 必须替换为 import ReactDOMServer from 'react-dom/server'

此外,uri 必须指向 http://localhost:3000/graphql 而不是 http://localhost:3000