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

时间:2017-06-29 02:31:32

标签: node.js express react-router apollo react-apollo

我正在使用Express运行Apollo / React,我正试图让服务器端渲染工作。 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();
    });

  });
});

使用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 */)
})

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

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

  

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

  

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

我几个小时以来一直在努力解决这个问题,但似乎无法弄明白。 similar problem的解决方案似乎与此案无关。任何帮助将不胜感激!

更多信息

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

  

POST / - - ms - -

     

(node:1938)UnhandledPromiseRejectionWarning:   未处理的承诺拒绝(拒绝ID:4443):错误:网络   错误:对http://localhost:3000的请求失败,原因:套接字挂断

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

  

/用户/.../ node_modules /双工器/ index.js:31

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

      ^
     

TypeError:无法读取未定义

的属性'on'      双工

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

     

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

     

在emitNone(events.js:91:20)

     

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

     

正在处理中。 (/用户/.../ node_modules / nodemon / LIB /监视器/ run.js:349:9)

     

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

     

在emitNone(events.js:86:13)

此外,端口3000是正确的。如果我改变号码,我会得到一个不同的错误:

  

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

2 个答案:

答案 0 :(得分:0)

您是在容器/容器内运行服务器/项目吗?我有同样的问题,最后做了以下修复。

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服务器通信,导致你在上面描述的问题SSR getaddrinfo ENOTFOUND,所以虽然它在客户端工作,但在SSR上它会失败。

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

答案 1 :(得分:0)

我终于发现错误是由于导致renderToString导致renderToStaticMarkupReactDOM无效。

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

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