Apollo-link-rest: `@rest` 指令在 SSR 环境中不起作用

创建于 2019-01-07  ·  9评论  ·  资料来源: apollographql/apollo-link-rest

你好!

我有一个在开发/浏览器中完美运行的@rest graphql 查询,但是当我在服务器(SSR)上运行相同的代码时,它会失败并显示一条长长的神秘错误消息。

(如果我禁用@rest指令,其他一切都可以在浏览器和 SSR 中完美运行)。

堆栈跟踪:

(node:26034) UnhandledPromiseRejectionWarning: Error: Network error: current.forEach is not a function
    at new ApolloError (.../demo-component/node_modules/src/errors/ApolloError.ts:56:5)
    at .../demo-component/node_modules/src/core/QueryManager.ts:512:31
    at .../demo-component/node_modules/src/core/QueryManager.ts:1046:11
    at Array.forEach (<anonymous>)
    at .../demo-component/node_modules/src/core/QueryManager.ts:1045:10
    at Map.forEach (<anonymous>)
    at QueryManager.broadcastQueries (.../demo-component/node_modules/src/core/QueryManager.ts:1039:18)
    at .../demo-component/node_modules/src/core/QueryManager.ts:411:18
(node:26034) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 5)
(node:26034) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

请指教 :/

最有用的评论

在我的引导文件中添加这个解决了我所有的问题:D

if (global.Headers == null) {
    const fetch = require('node-fetch');
    global.Headers = fetch.Headers;
}

注意使用node-fetch而不是fetch-headers解决了这个问题!

所有9条评论

再深入一点,我只是想在 NodeJS 中执行查询,而不用担心 SSR 等,

并有这个代码:

            client
                .query({query: Site, variables: {domain: "demo.example.com"}})
                .then(
                    () => console.log("Fullfilled"),
                    ({graphQLErrors, networkError, message, extraInfo}) =>
                        console.error("Rejected", graphQLErrors, networkError, message,extraInfo)
                )

有了这个结果:

Rejected [] TypeError: current.forEach is not a function
    at /home/richard/Projects/skil/demo-component/node_modules/src/restLink.ts:671:13
    at Array.reduce (<anonymous>)
    at concatHeadersMergePolicy (/home/richard/Projects/skil/demo-component/node_modules/src/restLink.ts:664:23)
    at RestLink.request (/home/richard/Projects/skil/demo-component/node_modules/src/restLink.ts:1235:21)
    at ApolloLink.request (/home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:76:19)
    at /home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:78:26
    at ApolloLink.request (/home/richard/Projects/skil/demo-component/node_modules/src/ApolloClient.ts:139:16)
    at ApolloLink.request (/home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:76:19)
    at /home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:78:26
    at DedupLink.request (/home/richard/Projects/skil/demo-component/node_modules/apollo-link-dedup/src/dedupLink.ts:39:30) Network error: current.forEach is not a function undefined

restLink.ts:671是这段代码:

export const concatHeadersMergePolicy: RestLink.HeadersMergePolicy = (
  ...headerGroups: Headers[]
): Headers => {
  return headerGroups.reduce((accumulator, current) => {
    if (!current) {
      return accumulator;
    }
    if (!current.forEach) {
      current = normalizeHeaders(current);
    }
    current.forEach((value, key) => {    // <-- This line here
      accumulator.append(key, value);
    });

    return accumulator;
  }, new Headers());
};

https://github.com/apollographql/apollo-link-rest/blob/master/src/restLink.ts#L671

另外,如果我用这个替换失败的forEach

    Object.keys(current).forEach(key => {
      accumulator.append(key, current[key])
    })

一切都恢复正常...

挑战,我不明白上面几行的目的:

    if (!current.forEach) {
      current = normalizeHeaders(current);
    }

因为这总是会失败,并且总是执行normalizeHeaders()

我将在这里冒险并猜测 Node 对new Headers()的使用感到不安。 一个新的 Headers 实例在其原型上有一个forEach方法。 如果提供给 reduce 的对象缺少 forEach 方法,我们执行normalizeHeaders 。 这确定变量是否是 Headers 的实例。 如果没有,它将再次使用new Headers()生成一组新的标头,从而导致您在此处看到的问题。

您的Object.keys(current).forEach()示例正在运行,因为它成功地将普通对象转换为数组,允许您对其调用.forEach()

下一步调试明智... normalizeHeaders()在您的 SSR 应用程序中的结果是什么? 看看它输出了什么,我猜你会发现它是一个普通的对象,而不是 Headers 的一个实例。

据我所知,我不相信 SSR 已经过全面测试(参见 #155),但@fbartho可能会对此有所了解。

SSR 目前不是直接测试或支持的功能。 理由:如果你有 SSR,你可以部署你自己的 Apollo GraphQL 服务器,并且你不需要 link-rest

然而,有些人已经想通了。 我并不反对改善那里世界状况的 PR。 在我看来,我们可能只需要一个 SSR 教程。

此外,根据您的运行时环境,需要对标头进行 polyfill 是相对常见的。 你试过了吗? @理查德87

@tombarton 的响应是正确的:该代码的目的是将标头哈希转换为适当的 Headers 类。

大家好!

感谢您对我的问题的关注;)

我的index.js的底部是这样的:

if (global.Headers == null) {
    global.Headers = require("fetch-headers");
}

// Set up babel to do its thing... env for the latest toys, react-app for CRA
// Notice three plugins: the first two allow us to use import rather than require, the third is for code splitting
// Polyfill is required for Babel 7, polyfill includes a custom regenerator runtime and core-js
require('@babel/polyfill');
require('@babel/register')({
    ignore: [/\/(build|node_modules)\//],
    presets: ['@babel/preset-env', '@babel/preset-react'],
    plugins: [
        '@babel/plugin-syntax-dynamic-import',
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-transform-instanceof',
    ]
});

// Now that the nonsense is over... load up the server entry point
require('./server');

所以我很确定我有 Header polyfill(也没有global.Headers我还有很多其他问题)......

我已经在我的 PHP 应用程序(API-Platform + https://webonyx.github.io/graphql-php/)中紧密集成了一个运行的 GraphQL 服务器,所以我不想安装额外的 Apollo GraphQL 服务器,我只想更快地加载 React 应用程序:D

我很难调试 restLink.ts,但我已经投入了一些战略性的console.log但它current _似乎是一个计划对象,但console.log可能会错过一些信息...

编辑...

export const concatHeadersMergePolicy: RestLink.HeadersMergePolicy = (
  ...headerGroups: Headers[]
): Headers => {
  return headerGroups.reduce((accumulator, current) => {
    if (!current) {
      return accumulator;
    }

    console.log(current.constructor.name)
    if (!current.forEach) {
      current = normalizeHeaders(current);
    }
    console.log(current.constructor.name)

    Object.keys(current).forEach(key => {
      accumulator.append(key, current[key])
    })

    return accumulator;
  }, new Headers());
};

打印出“标题”两次...

console.log(current.forEach)无论如何都会打印出 Undefined ,看来我的 Headers polyfill 没有forEach属性?

我的 PHPStorm 告诉我headers有一个 forEach 方法,但是在节点中运行此代码时,我得到一个headers.forEach is not a function

const Headers = require("fetch-headers");
const headers = new Headers();

console.log(headers)
console.log(headers.forEach)
console.log(!headers.forEach)

headers.forEach(h => console.log(h))

输出:

node /home/richard/Projects/demo-component/src/test.js 
Headers {}
undefined
true
/home/richard/Projects/demo-component/src/test.js:10
headers.forEach(h => console.log(h))
        ^

TypeError: headers.forEach is not a function
    at Object.<anonymous> (/home/richard/Projects/demo-component/src/test.js:10:9)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)

在我的引导文件中添加这个解决了我所有的问题:D

if (global.Headers == null) {
    const fetch = require('node-fetch');
    global.Headers = fetch.Headers;
}

注意使用node-fetch而不是fetch-headers解决了这个问题!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

sinisterra picture sinisterra  ·  6评论

Simply007 picture Simply007  ·  5评论

eranimo picture eranimo  ·  5评论

timhwang21 picture timhwang21  ·  7评论

MichelDiz picture MichelDiz  ·  7评论