Apollo-link: networkError [apollo-link-error] 的响应字段未定义

创建于 2018-11-06  ·  29评论  ·  资料来源: apollographql/apollo-link


onError函数或其文档中似乎存在错误。 这个问题已经在#698 中提到过,但我想我会稍微充实一下,希望能得到解决。

当使用onErrorapollo-boost或直接从apollo-link-error拦截错误时,文档指出错误对象包含response对象,可用于忽略错误。

我已经标记了文档复选框,因为这可能是预期的行为,但是由于文档在networkError部分的小段中提到了response对象,因此可能应该澄清一下案子。

预期行为
设置response.errors = null;应该可以防止错误传播而不会引发另一个错误。

实际行为
捕获networkError时, response字段未定义,尝试设置response.errors时会导致语法错误。 由于目前我无法理解的原因,这实际上确实阻止了网络错误的传播,而是引发了另一个错误。

在我的设置中(MacOS 10.14.1 w. Chrome 70.0.3538.77)设置response.errors = null将抛出Uncaught TypeError: Cannot set property 'errors' of undefinedresponse = { errors: null }将抛出Uncaught Error: "response" is read-only

skaermbillede 2018-11-06 kl 15 08 10

skaermbillede 2018-11-06 kl 15 09 20

一个简单的再现

以下是使用apollo-boost的复制品。 直接使用apollo-link-error也会出现同样的问题。

import ApolloClient from 'apollo-boost';

const client = ApolloClient({
  uri: '/my_api',
  onError: (({ response, networkError }) => {
    if (networkError && networkError.statusCode === 401) {
      console.log(response); // prints 'undefined'
      response.errors = null; // will throw 'undefined' error
      response = { errors: null }; // will throw a 'read-only' error 
    }
  })
});

问题标签

  • [ ] has-reproduction
  • [ ] 特征
  • [x] 文档
  • []阻止
  • [ ] 好第一期

最有用的评论

这个问题有什么进展吗?

所有29条评论

此错误是否有任何临时解决方法?

+1 如果您在服务器环境中使用 apollo 客户端(为您的解析器从不同的 GraphQL 服务器读取数据),那么这是一个很大的调试问题。

+1
是否有任何解决方法可以有条件地忽略错误?

我也有这个问题。

对我来说最大的问题是这会阻止 UI 意识到这个错误。 当实际出现大错误时,它仍然处于加载状态。

我已经对此进行了调查,根据文档,应该对回调做出响应:
https://www.apollographql.com/docs/link/links/error.html#callback

使用正常的回调会发生这种情况: https ://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L46

但是出现networkError: https ://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L62

关于何时合并的任何见解? :)

可能很快,apollo 团队就会专注于发布新的客户端和 react 版本。

这个问题有什么进展吗?

我也想知道这方面有进展吗? 或者在此期间的解决方法? 我想要一种将自定义错误消息从 API 传播到 UI 的方法。

我也面临同样的问题。 我的解决方法是返回空的 Observable。

我只是想悄悄地重定向到维护页面,以防服务器响应 503 - 不抛出异常。 这是我的代码,也许它会帮助某人:

import { Observable } from 'apollo-link';

...

onError(({ networkError }: any) => {
      if (networkError && networkError.status === 503) {
        this.redirectService.goToMaintenance();
        return Observable.of();
      }
});

...

@luki215我没有任何错误代码字段
image

@Sceat apollo-link你用的是哪个? 我使用apollo-angular-link-http ,所以这可能是不同的。

@luki215这实际上是我的错,使用 AWS api 网关我没有为200代码的任何其他内容正确配置 CORS 响应

对于遇到问题的人,您可以在默认 4xx 中添加 api 网关响应:
image

或在 serverless.yml
yml GatewayDefault4XX: Type: 'AWS::ApiGateway::GatewayResponse' Properties: ResponseParameters: gatewayresponse.header.Access-Control-Allow-Origin: "'*'" gatewayresponse.header.Access-Control-Allow-Headers: "'*'" ResponseType: DEFAULT_4XX RestApiId: Ref: 'ApiGatewayRestApi'

我相信我遇到了同样的绊脚石,并且无法将消息转发到我的反应应用程序中以创建错误消息。 在修复此问题方面有任何进展吗?

@strass不知道这个响应字段,但要处理错误,您可以打开网络错误

export default onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors) {
        for (let { extensions: { code } } of graphQLErrors)
            handleTheError(code)
    }
    if (networkError) {
        switch (networkError.statusCode) {
            case 500:
                //
                break
            case 429:
                //
                break
            case undefined:
                //
                break
            default:
                console.error(networkError)
                break
        }
    }
})

刚刚尝试了@luki215返回一个空的 Observable 的解决方法,但在我的情况下,这将使 react-apollo 抛出异常:

Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
    at Mutation._this.onMutationCompleted (react-apollo.esm.js:627)
    at react-apollo.esm.js:564

我正在使用它来改变返回给组件的错误,使用Mutation

const errorLink = onError(({ forward, networkError, operation }) => {
  if (networkError) {
    const { message: errorMessage } = networkError.result;
    switch (networkError.statusCode) {
      case 400:
        if (errorMessage) {
          networkError.message = errorMessage;
        }
        break;
      case 403:
        window.location = '/users/login';
        break;
      default:
        break;
    }
  }
  forward(operation);
});

我认为人们可能在这里寻找的关键是能够覆盖networkError中返回的错误字符串,并且通过这个networkError.message = 'Some custom error message.'完成

在深入研究apollo-link-rest和这个包之后想出了一个 hacky 解决方法。 我没有使用onError ,而是重写了自己的(如下)。

意图:拦截NetworkError并有条件地将其转换为GraphQLError 。 原因是,我使用的是不遵循 GraphQL 约定的 REST 端点,并返回带有 400 状态代码的{"message": "{ \"key\": \"not valid\"}"}之类的形状。 这不应被视为 NetworkError。

什么有效:此解决方案确实将响应中的实际错误消息向下传递,我可以在mutate承诺catch中检索和解析它并修改子表单的状态.

什么不起作用:

  • 我更愿意使用observer.next({ data: { ... }, errors: { ... } })以正确的方式传递错误,但它不起作用(该数据无处可去,并且突变承诺似乎没有解决)。
  • this之后依次调用observer.next()observer.error()并没有我所期望的行为。 只有错误调用似乎生效。
const errorLink = new ApolloLink((operation, forward) => {
    return new Observable(observer => {
      let sub: ZenObservable.Subscription;

      try {
        sub = forward!(operation).subscribe({
          next: result => {
            console.log("A proper result looks like:", result);
            observer.next(result)
          },
          error: networkError => {
            try {
              const { message } = networkError.result;
              const error = new GraphQLError(message);
              const apolloError = new ApolloError({
                errorMessage: "Some error happened",
                graphQLErrors: [error],
                networkError: undefined,
              });

              observer.error(new Error(message));
            } catch (_) {
              observer.error(networkError)
            }
          },
          complete: () => {
            console.log("Calling complete()");
            observer.complete();
          },
        });
      } catch (e) {
        console.log("ErrorLink had an error of its own")
        observer.error(e);
      }

      return () => {
        if (sub) sub.unsubscribe();
      };
    });
  });

在我看来,这应该可以工作,但mutate()函数似乎没有解决或拒绝 - 既没有调用then()也没有调用catch()

const errorLink = new ApolloLink((operation, forward) => {
    return new Observable(observer => {
      let sub: ZenObservable.Subscription;

      try {
        sub = forward!(operation).subscribe({
          next: result => {
            console.log("A proper result looks like:", result);
            observer.next(result)
          },
          error: networkError => {
            try {
              const { message } = networkError.result;
              const error = new GraphQLError(message);

              // this is called, but it seems to be the end of the line
              console.log("Intercepting error, returning success");
              observer.next({ data: {}, errors: [ error ]});
            } catch (localError) {
              console.error("Error in link:", localError);
              observer.error(networkError)
            }
          },
          complete: () => {
            console.log("Calling complete()");
            observer.complete();
          },
        });
      } catch (e) {
        console.log("ErrorLink had an error of its own")
        observer.error(e);
      }

      return () => {
        if (sub) sub.unsubscribe();
      };
    });
  });

只是推...
现在偶然发现同样的错误。
想要重置响应字段中的错误以处理组件本身的错误...

任何进展?

+1 在同一问题上。

我想我可能已经找到了解决方案(至少对于我的错误传播问题):

errorLink 中将map替换为forEach意味着错误可以将错误传播到 UI(以前 UI 组件中的 result.error 未定义,现在它有错误):

const link = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
-    graphQLErrors.map(({ message, locations, path }) =>
+    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
      ),
    );

  if (networkError) console.log(`[Network error]: ${networkError}`);
});

我也面临同样的问题。 我的解决方法是返回空的 Observable。

我只是想悄悄地重定向到维护页面,以防服务器响应 503 - 不抛出异常。 这是我的代码,也许它会帮助某人:

import { Observable } from 'apollo-link';

...

onError(({ networkError }: any) => {
      if (networkError && networkError.status === 503) {
        this.redirectService.goToMaintenance();
        return Observable.of();
      }
});

...

这对我有用,但它的作用与文档所说的完全相反,即“如果它想重试请求,错误回调可以选择从调用 forward(operation) 返回一个 observable。它不应该返回任何其他内容。”

今天更新 Apollo 后,我上面的解决方案不再通过 apollo-hooks 将 graphQL 错误传递到 UI。

有人有其他解决方案吗?

@strass您能解释一下为什么您的上述解决方案可以解决问题吗? IMO 他们使用forEachmap做完全相同的事情。

它不再(见 https://github.com/apollographql/apollo-link/issues/855#issuecomment-538010335 )

这方面有什么进展吗?

如果我们根本无法获得响应对象,我们该如何处理服务器返回带有“Location”标头的 302 以重新进行身份验证的情况?

如果有人有任何线索,请告诉我,因为我们正在考虑放弃阿波罗 :(

这极大地帮助了我https://github.com/apollographql/apollo-link/issues/297#issuecomment -350488527 并允许读取网络响应。

当身份验证层/网关 API 返回未经授权的响应时,这一点至关重要。

我很乐意将其视为核心功能的一部分!

这方面有什么进展吗? 这不可能是预期的行为。 还有什么方法可以将 GraphQL 错误转发给具有非 200 状态码的客户端?

只是给其他遇到捕获和修改网络错误问题的人的说明。 我试图将网络错误解包到 graphql 的内部错误,这就是我最终得到的结果:

const errorHandler = onError(({ graphQLErrors, networkError, operation, forward, response }) => {
    if (graphQLErrors) {
      console.error('apollo errors', graphQLErrors);
    }
    if (networkError) {
      console.error('apollo network errors', networkError);
      if (!!networkError['error'] && !!networkError['error']['errors'] && networkError['error']['errors'][0]) {
        console.error('unwrapping apollo network errors');
        networkError.message = networkError['error']['errors'][0].message;
        // you may also be able to set networkError.message to null based on criteria to remove the error, even if you can't prevent an error from being triggered altogether
      }
    }
  }
);

// Create an http link:
const httpLink = new HttpLink(this.httpClient).create({
  uri: this.uri,
  headers: new HttpHeaders({
    Authorization: this.token
  }),
  includeExtensions: true
});

const httpErrorLink = ApolloLink.from([
  errorHandler,
  httpLink,
]);

本质上,我将通用网络错误消息替换为第一个内部错误消息。 可能会帮助像我这样最终来到这里的人。

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