使用 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 之外做同样的事情。
我终于发现错误是由于 renderToString
和 renderToStaticMarkup
无法通过导入 ReactDOM
获得。
导入语句 import ReactDOM from 'react-dom'
必须替换为 import ReactDOMServer from 'react-dom/server'
。
此外,uri
必须指向 http://localhost:3000/graphql
而不是 http://localhost:3000
。
我 运行 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 之外做同样的事情。
我终于发现错误是由于 renderToString
和 renderToStaticMarkup
无法通过导入 ReactDOM
获得。
导入语句 import ReactDOM from 'react-dom'
必须替换为 import ReactDOMServer from 'react-dom/server'
。
此外,uri
必须指向 http://localhost:3000/graphql
而不是 http://localhost:3000
。