Apollo-link: networkError[apollo-link-error]の応答フィールドが未定義です

作成日 2018年11月06日  ·  29コメント  ·  ソース: apollographql/apollo-link


onError関数またはそのドキュメントにバグがあるようです。 この問題は#698ですでに言及されていますが、解決を期待して少し具体化すると思いました。

apollo-boostまたはapollo-link-errorから直接onErrorを使用してエラーをインターセプトする場合、ドキュメントには、エラーオブジェクトにresponseオブジェクトが含まれていると記載されています。エラーを無視します。

これは意図された動作である可能性があるため、ドキュメントのチェックボックスをオンにしましたが、ドキュメントではnetworkErrorセクションのサブ段落でresponseオブジェクトについて言及しているため、それがケース。

予想される行動
response.errors = null;を設定すると、別のエラーをスローせずにエラーが伝播するのを防ぐことができます。

実際の動作
networkErrorをキャッチすると、 responseフィールドが未定義になり、 response.errorsを設定しようとすると構文エラーが発生します。 現時点では私を超えている理由により、これは実際にはネットワークエラーの伝播を防ぎますが、代わりに別のエラーをスローします。

私のセットアップ(MacOS 10.14.1w。Chrome70.0.3538.77)では、 response.errors = nullを設定するとUncaught TypeError: Cannot set property 'errors' of undefined $がスローされ、 response = { 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 
    }
  })
});

発行ラベル

  • []持っている-複製
  • [ ] 特徴
  • [x]ドキュメント
  • []ブロッキング
  • []良い創刊号

docs

最も参考になるコメント

この問題に関する進展はありますか?

全てのコメント29件

このバグの一時的な回避策はありますか?

サーバー環境でapolloクライアントを使用している場合(リゾルバー用に別のGraphQLサーバーからデータを読み取る場合)、デバッグのかなり大きな問題を+1します。

+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に応答した場合に備えて、例外をスローせずに、maintanaceページに静かにリダイレクトしたかっただけです。 これが私のコードです、多分それは誰かを助けるでしょう:

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実際には私のせいでした。AWSAPIゲートウェイを使用して、 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'

私は同じ障害にぶつかっていて、エラーメッセージを作成するためにメッセージをreactアプリケーションに転送することができないと信じています。 この問題の修正に進展はありますか?

この応答フィールドについては@strassdunnoですが、エラーを処理するためにネットワークエラーをオンに切り替えることができます

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
        }
    }
})

空のObservableを返す@luki215の回避策を試しましたが、私の場合は、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とこのパッケージを掘り下げた後、ハッキーな回避策を思いつきました。 onErrorを使用するのではなく、自分で書き直しました(以下)。

目的: NetworkErrorをインターセプトし、条件付きでGraphQLErrorに変換します。 理由は、GraphQL規則に従わず、400ステータスコードで{"message": "{ \"key\": \"not valid\"}"}のような形状を返すRESTエンドポイントを使用しているためです。 これはNetworkErrorとして扱われるべきではありません。

動作:このソリューションは、 mutate promise catch内で取得して解析し、子フォームの状態を変更できるように、応答から実際のエラーメッセージをチェーンに渡します。 。

動作しないもの:

  • observer.next({ data: { ... }, errors: { ... } })を使用してエラーを正しい方法で渡すことをお勧めしますが、機能しません(そのデータはどこにも行かず、ミューテーションの約束は解決されないようです)。
  • 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。

私は解決策を見つけたかもしれないと思います(少なくとも私のエラー伝播の問題に対する):

errorLinkmapforEachに置き換えると、エラーが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に応答した場合に備えて、例外をスローせずに、maintanaceページに静かにリダイレクトしたかっただけです。 これが私のコードです、多分それは誰かを助けるでしょう:

import { Observable } from 'apollo-link';

...

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

...

これは私にとってはうまくいきましたが、ドキュメントに記載されていることとは正反対のことをしています。つまり、「エラーコールバックは、リクエストを再試行したい場合、オプションでforward(operation)の呼び出しからオブザーバブルを返すことができます。他に何も返さないはずです。」

今日Apolloを更新した後、上記の私のソリューションは、UIへのapolloフックを介してgraphQLエラーを伝達しなくなりました。

誰かが別の解決策を持っていますか?

@strassは、上記のソリューションで問題が解決する理由を説明できます。 IMOは、 forEachmapを使用してまったく同じことを行います。

もうありません(https://github.com/apollographql/apollo-link/issues/855#issuecomment-538010335を参照)

これについて何か進展はありますか?

応答オブジェクトをまったく取得できない場合に、サーバーが再認証のために「Location」ヘッダーを持つ302を返す場合にどのように対処するのでしょうか。

誰かが何か手がかりを持っているなら、私たちがアポロを落とすことを考えているcosを私に知らせてください:(

これは、 https: //github.com/apollographql/apollo-link/issues/297#issuecomment -350488527に大いに役立ち、ネットワーク応答を読み取ることができます。

認証レイヤー/ゲートウェイAPIから不正な応答が返される場合に重要なこと。

これをコア機能の一部として見たいです!

これについて何か進展はありますか? これが意図された動作である方法はありません。 他に、200以外のステータスコードでGraphQLエラーをクライアントに転送するにはどうすればよいですか?

ネットワークエラーのキャッチと変更で問題が発生した場合は、他の人に注意してください。 私はネットワークエラーを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 評価