Apollo-link: Antwortfeld ist für networkError [apollo-link-error] nicht definiert

Erstellt am 6. Nov. 2018  ·  29Kommentare  ·  Quelle: apollographql/apollo-link


Es scheint einen Fehler in der Funktion onError oder ihrer Dokumentation zu geben. Das Problem wurde bereits in #698 erwähnt, aber ich dachte, ich würde es in der Hoffnung auf eine Lösung ein wenig konkretisieren.

Beim Abfangen eines Fehlers mit onError entweder von apollo-boost oder direkt von apollo-link-error gibt die Dokumentation an, dass das Fehlerobjekt ein response -Objekt enthält, das verwendet werden kann Fehler ignorieren.

Ich habe das Docs -Kontrollkästchen markiert, da dies ein beabsichtigtes Verhalten sein könnte, aber da die Dokumentation das response -Objekt in einem Unterabsatz des networkError -Abschnitts erwähnt, sollte es wahrscheinlich ein wenig klargestellt werden, ob dies der Fall ist der Fall.

Erwartetes Verhalten
Das Festlegen response.errors = null; sollte verhindern, dass sich der Fehler ausbreitet, ohne einen weiteren Fehler auszulösen.

Tatsächliches Verhalten
Beim Fangen von networkError ist das Feld response undefiniert, was zu Syntaxfehlern führt, wenn versucht wird, response.errors . Aus Gründen, die mir im Moment ein Rätsel sind, verhindert dies tatsächlich, dass sich der Netzwerkfehler ausbreitet, sondern wirft stattdessen einen anderen Fehler.

In meinem Setup (MacOS 10.14.1 mit Chrome 70.0.3538.77) wirft die Einstellung response.errors = null ein Uncaught TypeError: Cannot set property 'errors' of undefined und response = { errors: null } wirft ein Uncaught Error: "response" is read-only .

skaermbillede 2018-11-06 kl 15 08 10

skaermbillede 2018-11-06 kl 15 09 20

Eine _einfache_ Reproduktion

Das Folgende ist eine Reproduktion mit apollo-boost . Das gleiche Problem tritt bei direkter Verwendung 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 
    }
  })
});

Ausgabeetiketten

  • [ ] hat-Reproduktion
  • [ ] Merkmal
  • [x] Dokumente
  • [ ] blockiert
  • [ ] gute erste ausgabe

docs

Hilfreichster Kommentar

Irgendwelche Fortschritte zu diesem Thema?

Alle 29 Kommentare

Gibt es eine vorübergehende Problemumgehung für diesen Fehler?

+1 ein ziemlich großes Problem beim Debuggen, wenn Sie den Apollo-Client in der Serverumgebung verwenden (Daten von einem anderen GraphQL-Server für Ihre Resolver lesen).

+1
Gibt es eine Problemumgehung, um Fehler bedingt zu ignorieren?

Ich habe auch dieses Problem.

Das große Problem für mich ist, dass dies verhindert, dass die Benutzeroberfläche diesen Fehler erkennt. Es bleibt im Ladezustand, wenn tatsächlich ein großer Fehler vorliegt.

Irgendwelche Erkenntnisse darüber, wann es zusammengeführt wird? :)

Wahrscheinlich schon bald konzentriert sich das apollo-Team auf die Veröffentlichung der neuen Client- und React-Version.

Irgendwelche Fortschritte zu diesem Thema?

Ich frage mich auch, ob es diesbezüglich Fortschritte gibt? Oder zwischenzeitlich ein Workaround? Ich hätte gerne eine Möglichkeit, benutzerdefinierte Fehlermeldungen von der API in die Benutzeroberfläche zu übertragen.

Ich stand auch vor dem gleichen Problem. Problemumgehung für mich war, leeres Observable zurückzugeben.

Ich wollte nur leise auf die Wartungsseite umleiten, falls der Server 503 antwortet - ohne eine Ausnahme auszulösen. Hier ist mein Code, vielleicht hilft es jemandem:

import { Observable } from 'apollo-link';

...

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

...

@ luki215 Ich habe kein Fehlercodefeld
image

@Sceat welches apollo-link verwendest du? Ich verwende apollo-angular-link-http , das könnte also der Unterschied sein.

@ luki215 es war eigentlich meine Schuld, mit dem AWS api Gateway habe ich die CORS-Antwort für alles andere, was 200 Codes enthält, nicht richtig konfiguriert

Für Leute, die auf das Problem stoßen, können Sie API-Gateway-Antworten in Standard 4xx hinzufügen:
image

oder in 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'

Ich glaube, ich treffe auf denselben Stolperpunkt und kann keine Nachrichten an meine Reaktionsanwendung weiterleiten, um damit Fehlermeldungen zu erstellen. Gibt es Fortschritte bei der Lösung dieses Problems?

@strass weiß nichts über dieses Antwortfeld, aber um Fehler zu behandeln, können Sie Netzwerkfehler einschalten

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

Ich habe gerade die Problemumgehung von @ luki215 versucht, ein leeres Observable zurückzugeben, aber in meinem Fall wird das jetzt dazu führen, dass "react-apollo" eine Ausnahme auslöst:

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

Ich verwende dies, um den an die Komponente zurückgegebenen Fehler mit Mutation zu mutieren

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

Ich denke, das Wichtigste, wonach die Leute hier vielleicht suchen, ist die Möglichkeit, die in networkError zurückgegebene Fehlerzeichenfolge zu überschreiben, und das ist mit diesem networkError.message = 'Some custom error message.' erledigt

Kam mit einer hackigen Problemumgehung, nachdem ich mich mit apollo-link-rest und diesem Paket beschäftigt hatte. Anstatt onError zu verwenden, habe ich mein eigenes umgeschrieben (unten).

Die Absicht: ein NetworkError abfangen und es bedingt in ein GraphQLError umwandeln. Der Grund dafür ist, dass ich einen REST-Endpunkt verwende, der nicht der GraphQL-Konvention folgt und Formen wie {"message": "{ \"key\": \"not valid\"}"} mit einem 400-Statuscode zurückgibt. Das sollte nicht als NetworkError behandelt werden.

Was funktioniert: Diese Lösung gibt die eigentliche Fehlermeldung von der Antwort in der Kette weiter, sodass ich sie innerhalb des mutate -Versprechens catch abrufen und analysieren und den Zustand des untergeordneten Formulars ändern kann .

Was nicht funktioniert:

  • Ich würde es vorziehen, observer.next({ data: { ... }, errors: { ... } }) zu verwenden, um die Fehler richtig weiterzugeben, aber es funktioniert nicht (diese Daten gehen nirgendwo hin und das Mutationsversprechen scheint sich nicht aufzulösen).
  • Der Aufruf observer.next() und observer.error() nacheinander hat nicht das Verhalten, das ich erwartet hatte, nach diesem . Nur der Fehleraufruf scheint zu wirken.
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();
      };
    });
  });

Es scheint mir so, als ob dies funktionieren sollte, aber die Funktion mutate() scheint nicht aufgelöst oder abgelehnt zu werden - weder then() noch catch() werden aufgerufen.

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

Einfach schieben...
Stolpere jetzt über den gleichen Fehler.
Möchten Sie den Fehler im Antwortfeld zurücksetzen, um den Fehler an der Komponente selbst zu behandeln ...

Irgendein Fortschritt?

+1 zum selben Thema.

Ich glaube, ich habe vielleicht eine Lösung gefunden (zumindest für mein Problem mit der Fehlerfortpflanzung):

Das Ersetzen map durch forEach in errorLink bedeutet, dass Fehler Fehler an die Benutzeroberfläche weitergeben können (zuvor war result.error in der UI-Komponente undefiniert, jetzt hat es den/die Fehler):

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

Ich stand auch vor dem gleichen Problem. Problemumgehung für mich war, leeres Observable zurückzugeben.

Ich wollte nur leise auf die Wartungsseite umleiten, falls der Server 503 antwortet - ohne eine Ausnahme auszulösen. Hier ist mein Code, vielleicht hilft es jemandem:

import { Observable } from 'apollo-link';

...

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

...

Dies hat für mich funktioniert, aber es macht genau das Gegenteil von dem, was die Dokumentation sagt, dh "Der Fehler-Callback kann optional ein Observable aus dem Aufruf von forward (operation) zurückgeben, wenn er die Anfrage wiederholen möchte. Es sollte nichts anderes zurückgeben."

Nachdem ich Apollo heute aktualisiert habe, überträgt meine obige Lösung keine graphQL-Fehler mehr über Apollo-Hooks an die Benutzeroberfläche.

Hat jemand eine andere Lösung?

@strass können Sie erklären, warum Ihre obige Lösung das Problem behebt. Meiner Meinung nach machen sie genau dasselbe mit forEach und map .

Es funktioniert nicht mehr (siehe https://github.com/apollographql/apollo-link/issues/855#issuecomment-538010335 )

Irgendwelche Fortschritte dabei?

Wie sollen wir mit einem Fall umgehen, in dem der Server einen 302 mit einem „Location“-Header zurückgibt, um sich erneut zu authentifizieren, wenn wir das Antwortobjekt überhaupt nicht erhalten können?

Wenn jemand eine Ahnung hat, lass es mich wissen, denn wir denken darüber nach, Apollo fallen zu lassen :(

Dies hat mir massiv geholfen https://github.com/apollographql/apollo-link/issues/297#issuecomment -350488527 und ermöglicht das Lesen von Netzwerkantworten.

Dies ist entscheidend, wenn nicht autorisierte Antworten von Authentifizierungsebenen/Gateway-APIs zurückgegeben werden.

Ich würde dies gerne als Teil der Kernfunktionalität sehen!

Irgendwelche Fortschritte dabei? Dies ist auf keinen Fall das beabsichtigte Verhalten. Wie sonst kann man GraphQL-Fehler mit einem Statuscode ungleich 200 an den Client weiterleiten?

Nur ein Hinweis für alle anderen, die auf ein Problem mit dem Abfangen und Ändern von Netzwerkfehlern stoßen. Ich habe versucht, Netzwerkfehler in den inneren Fehler von graphql zu entpacken, und hier ist, was ich am Ende hatte:

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,
]);

Im Wesentlichen ersetze ich die Meldung, die eine generische Netzwerkfehlermeldung ist, durch die erste interne Fehlermeldung. Könnte einigen Leuten wie mir helfen, die hier gelandet sind.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen