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

Posted

技术标签:

【中文标题】使用 Apollo 进行服务器端渲染:getaddrinfo ENOTFOUND【英文标题】:Server side rendering with Apollo: getaddrinfo ENOTFOUND 【发布时间】:2017-12-02 13:58:21 【问题描述】:

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

app.use((req, res) => 
  match( routes, location: req.originalUrl , (error, redirectLocation, renderProps) => 
    const client = new ApolloClient(
      s-s-rMode: 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();
    );

  );
);

它使用来自 React Router v3 的 match() 函数(如从文档链接的“GitHunt”示例中的 package.json 所证明的那样)。我正在使用没有match() 的React Router v4,所以我使用react-router-config package 中的renderRoutes() 重构代码如下。

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 */)
)

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

当我访问http://localhost:3000 时,页面按预期加载,我可以点击指向子目录的链接(例如http://localhost:3000/folder)。 当我尝试通过在地址栏中输入名称直接加载子目录时,我的浏览器一直在等待服务器响应。大约六秒钟后,终端显示以下错误之一(不确定是什么导致错误在后续尝试中发生变化):

(node:1938) UnhandledPromiseRejectionWarning: 未处理的承诺 拒绝(拒绝 id:1):错误:网络错误:请求 http://localhost:3000 失败,原因:getaddrinfo ENOTFOUND localhost 本地主机:3000

(node:8691) UnhandledPromiseRejectionWarning: 未处理的承诺 拒绝(拒绝 id:1):错误:网络错误:请求 http://localhost:3000失败,原因:socket挂断

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

更多信息

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

POST / - - 毫秒 - -

(节点:1938)UnhandledPromiseRejectionWarning: 未处理的承诺拒绝(拒绝 id:4443):错误:网络 错误:对http://localhost:3000的请求失败,原因:socket挂断

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

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

writer.on("drain", function() 

      ^

TypeError:无法读取未定义的“on”属性

在双工 (/Users/.../node_modules/duplexer/index.js:31:11)

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

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

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

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

在 emitNone (events.js:91:20)

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

在过程中。 (/Users/.../node_modules/nodemon/lib/monitor/run.js:349:9)

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

在 emitNone (events.js:86:13)

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

(node:2056) UnhandledPromiseRejectionWarning: 未处理的承诺 拒绝(拒绝 id:1):错误:网络错误:请求 http://localhost:3010 失败,原因:连接 ECONNREFUSED 127.0.0.1:3010

【问题讨论】:

你试过http://127.0.0.1:3000而不是localhost吗?此外,您应该排除防病毒软件和/或防火墙。并确保在您的 hosts 文件中有一个条目 127.0.0.1 localhost 我尝试了127.0.0.1localhost,有和没有http://,但事实并非如此。我还仔细检查了主机文件中的条目。但是我注意到问题仅在直接访问子文件夹并相应更新问题时才会显现出来。谢谢 将 s-s-rMode: true 添加到 ApolloClient,我遇到了类似的问题,根本原因是重复获取。希望对您有所帮助。 您的意思是const client = new ApolloClient(/* ... */)?因为我已经在其中包含了s-s-rMode: true,所以我只是省略了第二个代码 sn-p 中的参数,因为它们与第一个代码中的参数相同。还是谢谢! 你使用的node版本和操作系统是什么? 【参考方案1】:

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

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

我在 docker-compose.yml 中为我的容器创建了一个内部 docker 网络,它允许容器相互通信,但是浏览器通过不同的 URL 与 GQL 服务器通信,导致您在s-s-r getaddrinfo ENOTFOUND,所以虽然它在客户端工作,但在 s-s-r 上它会失败。

我正在使用能够检测浏览器或 s-s-r 的 nextJS 框架,我相信您可以在 nextJS 之外执行相同的操作。

【讨论】:

谢谢。不幸的是,我没有在(一个)容器中运行我的服务器。 这是我一直在寻找的解决方案 :)【参考方案2】:

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

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

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

【讨论】:

以上是关于使用 Apollo 进行服务器端渲染:getaddrinfo ENOTFOUND的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 NextJS 的 Apollo Client 时服务器端渲染数据?

react-apollo 服务器端渲染

使用 s-s-r 和 Apollo 客户端时防止客户端重新渲染

使用 redux 状态以及 Apollo 的 Graphql

Apollo 客户端查询报错:“Network error: Failed to fetch”如何排查?

Nuxt.js 服务器端渲染:启动服务器后数据未更新