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
.
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
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.
Ich habe dies untersucht und laut den Dokumenten sollte eine Antwort auf den Rückruf gegeben werden:
https://www.apollographql.com/docs/link/links/error.html#callback
Beim normalen Callback passiert das: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L46
Aber mit dem Netzwerkfehler: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L62
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
@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:
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:
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).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.
Hilfreichster Kommentar
Irgendwelche Fortschritte zu diesem Thema?