Apollo-link: networkError bei apollo-link-error hat kein Statusattribut

Erstellt am 2. Dez. 2017  ·  35Kommentare  ·  Quelle: apollographql/apollo-link

Als Thema. Vielen Dank

const errorLink = onError(({ networkError }) => {
  if (networkError.status === 401) {
    logout();
  }
})

screen shot 2017-12-03 at 4 49 20 am

Hilfreichster Kommentar

Also für alle die noch suchen...

Der statusCode-Wert wird als Eigenschaft für networkError zurückgegeben, und die aktualisierte Typisierung für networkError wurde hier hinzugefügt: https://github.com/apollographql/apollo-link/pull/530

Da networkError jetzt ein Union-Typ von Error | ServerError | ServerParseError , erlaubt uns Typescript nur den Zugriff auf Eigenschaften, die allen Typen in der Union gemeinsam sind. statusCode existiert nicht auf Typ Error . Wir werden nicht über networkError.statusCode darauf zugreifen können, ohne dass sich Typescript beschwert; wir müssen einen Typenwächter oder etwas anderes verwenden, um die Typen zu unterscheiden.

Im Typescript-Handbuch werden mehrere Methoden vorgeschlagen: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types

Für meine Zwecke habe ich den Operator in , um die Dinge zum Laufen zu bringen.

  onError: ({ networkError }: ErrorResponse) => {
    if (
      networkError &&
      'statusCode' in networkError &&
      networkError.statusCode === 401
    ) {
      // perform logout stuff
    }
  },

@JoviDeCroock Denken

Alle 35 Kommentare

Wie ich weiß, tritt "Fehler beim Abrufen" auf, wenn keine Antwort erfolgt (z. B. Server ist ausgefallen). Das liegt daran, dass Sie keinen Statuscode sehen. Bitte korrigiert mich, wenn ich falsch liege.

Aber ich denke, in solchen Situationen ist es nicht die beste Idee, nur eine SMS zu haben - es sollte etwas geben, das einfacher zu handhaben ist (wie ein Fehlercode).

Ich habe gesehen, dass die Anfrage (401) im Netzwerk-Tag des Chrome-Inspektors gesendet wurde.

Ich denke, das hängt mit #218 zusammen (nicht sicher..)

Habe auch dieses Problem. networkError.status oder networkError.statusCode fehlen und das Antwortobjekt fehlt. Es gibt keine Möglichkeit, HTTP-Statuscodes in der Netzwerkschicht zu verarbeiten.

Dieses Problem habe ich aktuell auch. Es wurde angeblich in der neuesten Version behoben, aber ich habe immer noch dieses Problem.

irgendwelche Gedanken zu diesem @jbaxleyiii ?

Es tut mir leid zu hören, dass Sie @akrigline und @bogdansoare immer noch Probleme damit haben! Dies sollte auf jeden Fall behoben werden.

Ich arbeite gerade durch #364. Könnten Sie in der Zwischenzeit diese

irgendwelche Neuigkeiten?

Bei diesem Problem ist auch das Antwortobjekt leer und es gibt keinen statusCode für das networkError-Objekt.

Ich bekomme nicht einmal die Eigenschaft networkError :
screen shot 2018-05-07 at 13 01 56

EDIT: Höchstwahrscheinlich #475

Ich habe dieses Problem behoben, indem ich den Typ Error ignoriert habe. Dies ist jedoch eine vollständig vorübergehende Lösung und wir müssen auf zukünftige Korrekturen warten. @evans

Hier ist dieses Problem!

Der aktuelle der networkError , die wir über sie geschrieben haben, war kein echter serverseitiger Fehler. Zusammenfassend haben wir in diesen Fällen überhaupt keine statusCode und tatsächlich müssen wir einen echten Netzwerkfehler auf dem graphql-Server erstellen oder empfangen. (_Wenn Sie nur ein Kommunikationsproblem mit dem Server haben, können Sie nicht auf statusCode _ zugreifen).

screen shot 2018-05-07 at 20 38 09

Schriftfehler bleibt bestehen!

Wenn Sie TypeScript verwenden, können Sie sehen, dass networkError den folgenden Typ hat:

export interface ErrorResponse {
  graphQLErrors?: GraphQLError[];
  networkError?: Error;
  response?: ExecutionResult;
  operation: Operation;
}

Und Error ist definiert als:

interface Error {
    name: string;
    message: string;
    stack?: string;
}

Wir sehen, dass statusCode hier nicht definiert ist, also können wir in TypeScript nicht so etwas wie networkError.statusCode aufrufen.

Meine Übergangslösung

Wir müssen networkError als any und auch als leeres object .

const afterWareLink = onError(({operation, response, graphQLErrors = {}, networkError = {} as any}) => {
    const status: number = networkError && networkError.statusCode ? networkError.statusCode : null;
    debugHelper.error('apolloError', {
        operation,
        response,
        graphQLErrors,
        networkError,
        status,
    });

    // Do your job
    // if (status && HTTPExceptions[status])
    //     redirectService.redirectWithReload(`/error/${status}`);
});

Gemäß den obigen afterWareLink Codes sind wir auf einen Serverfehler mit dem 400-Statuscode gestoßen und können jetzt auf diesen Statuscode zugreifen:

screen shot 2018-05-07 at 20 28 03

Hier ist meine ApolloClient-Definition:

import {ApolloLink, from} from 'apollo-link';
import {application} from '../../constants/application';
import {ApolloClient} from 'apollo-client';
import {InMemoryCache} from 'apollo-cache-inmemory';
import {HttpLink} from 'apollo-link-http';
import {tokenStorage} from '../middleware';
import {debugHelper} from '../helpers/debugHelper';
import {onError} from 'apollo-link-error';
import {apolloCache} from './apolloCache';
import fetch from 'unfetch';

const API = new HttpLink({
    uri: application.API.URL,
    fetch: fetch
});

const cache = new InMemoryCache({
    dataIdFromObject: (object: any) => apolloCache.getID(object)
});

const afterWareLink = onError(({operation, response, graphQLErrors = {}, networkError = {} as any}) => {
    const status: number = networkError && networkError.statusCode ? networkError.statusCode : null;
    debugHelper.error('apolloError', {
        operation,
        response,
        graphQLErrors,
        networkError,
        status,
    });

    // Do your job
    // if (status && HTTPExceptions[status])
    //     redirectService.redirectWithReload(`/error/${status}`);
});

const middleWareLink = new ApolloLink((operation, forward) => {
    const token = tokenStorage.get();
    operation.setContext(context => ({
        ...context,
        headers: {
            ...context.headers,
            token: token ? token.token : '',
        },
    }));

    return forward(operation);
});

export const apolloClient = new ApolloClient({
    link: from([
        middleWareLink,
        afterWareLink,
        API
    ]),
    cache: cache,
});

Hoffe, nützlich zu sein.

Wenn Sie einen Knotenserver verwenden und über den Kontext Zugriff auf Ihr Antwortobjekt haben, können Sie dies lösen, indem Sie Folgendes hinzufügen; ctx.res.status(401); bevor Sie Ihre Antwort zurücksenden.
Jetzt können sowohl apollo-link-errors als auch apollo-link-retry den Fehler vom networkErrors-Objekt abholen.

Hier ist ein Beispiel, in dem ich eine benutzerdefinierte Schemadirektive verwende, um sicherzustellen, dass ein Benutzer authentifiziert ist.

import { SchemaDirectiveVisitor } from "graphql-tools";
import { defaultFieldResolver } from "graphql";
import { createError } from "apollo-errors";

const AuthError = createError("AuthError", {
  message: "Not authorized"
});

export class IsAuthenticatedDirective extends SchemaDirectiveVisitor {
  visitObject(type) {
    this.ensureFieldsWrapped(type);
  }
  visitFieldDefinition(field, details) {
    this.ensureFieldsWrapped(details.objectType);
  }

  ensureFieldsWrapped(objectType) {
    if (objectType._authFieldsWrapped) return;
    objectType._authFieldsWrapped = true;

    const fields = objectType.getFields();
    Object.keys(fields).forEach(fieldName => {
      const field = fields[fieldName];
      const { resolve = defaultFieldResolver } = field;
      field.resolve = async function(...args) {
        const ctx = args[2];
        const result = await resolve.apply(this, args);
        if (ctx.req.user) {
          return result;
        } else {
          ctx.res.status(401);
          return null;
        }
      };
    });
  }
}

War es behoben oder nicht? Ich kann immer noch auf keinen Statuscode im networkError-Objekt zugreifen.

@artsiompeshko
In TypeScript kann ich so darauf zugreifen:

const networkErrorLink = onError(({networkError, operation, forward}: ErrorResponse) => {
  if (networkError) {
    switch (networkError['statusCode']) {
      case 401:
      case 422:
        if (window.location.pathname !== '/login') {
          Logger.error('Unauthorized or stale Authorization Session. Reloading page...')
          window.history.go()
        }
        break
    }
  }
  return forward(operation)
})

Gibt es darüber irgendwelche Neuigkeiten? Gleiches Problem. In meinem Javascript-Code ist kein networkError.statusCode verfügbar.

@goldenbearkin liegt es vielleicht falsch, dass wir es richtig verwenden?

Wir sollten den Netzwerkstatuscode im Fehler zulassen, derzeit gibt es keine Möglichkeit, auf den Netzwerkstatus zuzugreifen. Wenn networkError eigentlich ein Objekt vom Typ HttpErrorResponse ist, das eine status Eigenschaft hat, die immer 0 wobei der ursprüngliche Statuscode in der Konsole 401 oder 403 ist

@lvlohammadi
Kann die gleiche Problemumgehung auch für graphQLErrors durchgeführt werden? Ich kann den Statuscode für networkErrors mit Ihrer Problemumgehung sehen - habe dies mit einem 504 getestet. Aber wenn die Authentifizierung fehlschlägt, erwarte ich einen graphQLError-Statuscode von 401. Aber derzeit keine Möglichkeit, darauf zuzugreifen. Ich habe die Option 'graphQLErrors = {} as any' ausprobiert, aber ohne Erfolg.

Das ist seltsam, aber ich habe tatsächlich statusCode Eigenschaft auf networkError . Es gibt jedoch keine solche Eigenschaft in der Schnittstelle, daher gibt TS hier einen Fehler aus.

const errorLink = onError(({ networkError, graphQLErrors, response }: ErrorResponse) => {
  if (graphQLErrors) {
    graphQLErrors.map(({ message, locations, path }: GraphQLError) => {
      showError(message);
    });
  }

  // @ts-ignore
  console.log('statusCode', networkError.statusCode); // outputs 401

  if (networkError) {
    showError(networkError.message);
  }
});

Auf dem Server verwende ich koa und koa-passport , und der entsprechende Code hier ist:

app.use(mount(graphQlPath, passport.authenticate('jwt', { session: false })));

Da dieses Problem wirklich veraltet ist, schließe ich es, aber wenn Sie sich weiterhin Sorgen machen, können Sie es gerne wieder öffnen und ich werde mich so schnell wie möglich bei Ihnen melden.

@JoviDeCroock steht

Alles klar, Sie sind sich also sicher, dass es sich um einen networkError (kein graphqlError?) handelt und statusCode/status undefiniert ist?

Ist es ein Schreibfehler oder wirklich undefiniert? Kannst du es reproduzieren?

Übrigens hilft das Downvoten meines Kommentars nicht bei der Behebung dieses Fehlers, es gab nie eine Möglichkeit, dieses Problem zu reproduzieren, und die Probleme mussten gekürzt werden, um die Art und Weise zu verbessern, wie wir helfen können.

Ich habe das gleiche Problem, kann jedoch daran liegen, wie ich den Status vom Server sende. Ich versuche im Grunde genommen, jede der graphQL-spezifischen Behandlungen abzukürzen und einfach zu antworten, zum Beispiel mit:
res.status(404).send("The resource you are looking for cannot be found")

Allerdings bekomme ich nur TypeError: Failed To Fetch . Liegt das an Implementierungsdetails in Apollo-Server-Express?

@JoviDeCroock Eigentlich habe ich ein wenig mit graphql-Code vermasselt, als ich versuchte, die clientseitige Fehlerbehandlung für 401 oder 403 zu implementieren. Wir verwenden Middleware, und ich habe einfach immer 401 für jede Anfrage zurückgegeben. So bekam auch die OPTION-Anfrage 401, was nicht passieren sollte, und wahrscheinlich konnte ich deswegen den Status überhaupt nicht sehen. Nachdem ich den Code aktualisiert habe, kann ich Statuscodes sehen

Vielen Dank für das Update @dimitriy-k freut sich zu hören, dass dieses Problem behoben ist.

Für @AlbertoPL werde ich mich heute Abend mit Ihrem Problem

@JoviDeCroock Danke für den Blick. Der spezifische Code, den ich zum Laufen bringen möchte, ist also der folgende:
res.status(426).send()

Auf der Registerkarte "Netzwerk" von Chrome sehe ich, dass der Endpunkt /graphql mit einem 426-Statuscode und einer entsprechenden Antwort (Upgrade erforderlich) antwortet. Ich sehe jedoch immer noch nur TypeError: Failed to Fetch wenn ich den onError-Link von apollo-link-error . Wenn ich den wahren Statuscode bekommen könnte, wäre das großartig (oder sogar die Nachricht würde ausreichen).

Alles klar, das sind einige ziemlich gute Informationen. Wir sollten das irgendwie debuggen können, fühlen Sie sich dem gewachsen oder?

Ja, ich kann es auf jeden Fall versuchen. Über Hilfe/Tipps würde ich mich freuen.

Wenn ich also nur den Code im Chrome-Debugger durchgehe, kann ich sehen, dass sowohl apollo-link-batch-http als auch apollo-link-http API-Aufrufe mit der Abruffunktion ausführen (entweder als Linkoption oder als Standardeinstellung übergeben, ich habe beide ausprobiert):

fetcher(chosenURI, options)
    .then(function (response) {
        operations.forEach(function (operation) { return operation.setContext({ response: response }); });
        return response;
    }).then(parseAndCheckHttpResponse(operations))

Es geht nie in einen der Then-Blöcke, sondern in den Catch-Block. Bis dahin ist der Fehler bereits auf "Failed to Fetch" gesetzt. Es ist kein Statuscode vorhanden.

Wir sollten hier prüfen, ob es schief geht:

https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http-common/src/index.ts#L118

Ich werde versuchen, dieses Wochenende eine Reproduktion zu machen, da ich einen ziemlich vollen Terminkalender bei der Arbeit habe.

BEARBEITEN:

Das Hauptproblem ist, dass ich jedes Mal, wenn ich dies teste, apollo-server und es funktioniert gut, aber es könnte sein, dass die Bindungen oder etwas anders sind. Daher fällt es mir schwer das ohne Server als Reproduktion zu testen

Wenn es hilft, hier ist unser Apollo-Server (beachten Sie, dass wir Apollo-Server-Express verwenden). CreateGraphQLSchema gibt die von uns verwendete Instanz von Apollo Server zurück:

const { ApolloServer, gql } = require("apollo-server-express");

const collectFragments = container => {
  return container
    .$list()
    .map(component => container[component])
    .join("\n");
};

const mergeObjects = container => {
  const types = container.$list().map(k => container[k]);
  return _.merge(...types);
};

function CreateGraphQLSchema(
  schema,
  queries,
  mutations,
  subscriptions,
  resolvers,
  graphqlFormatError,
) {
  const graphQlSchema = gql`
    ${collectFragments(schema.enum)}

    ${collectFragments(schema.type)}

    type Query {
      ${schema.Query}
    }

    ${collectFragments(schema.input)}

    type Mutation {
      ${schema.Mutation}
    }

    type Subscription {
      ${schema.Subscription}
    }

    schema {
      query: Query
      mutation: Mutation
      subscription: Subscription
    }
  `;

  const resolverMap = {};
  resolverMap.Query = mergeObjects(queries);
  resolverMap.Mutation = mergeObjects(mutations);
  resolverMap.Subscription = mergeObjects(subscriptions);
  resolvers.$list().forEach(type => (resolverMap[type] = resolvers[type]));

  return new ApolloServer({
    typeDefs: graphQlSchema,
    resolvers: resolverMap,
    context: ({ req, res }) => {
      return { req, res, user: req.user };
    },
    formatError: graphqlFormatError,
    playground: process.env.NODE_ENV !== "production",
  });
}

@AlbertoPL Ich bin auch auf dieses Problem CORS-Problem handelt , nicht um einen Apollo-Client.

Ein fetch() Promise wird mit einem TypeError abgelehnt, wenn ein Netzwerkfehler auftritt oder CORS serverseitig falsch konfiguriert ist

Stellen Sie sicher, dass Sie die richtigen Header zurückgeben, wenn Sie die Anfrage ablehnen. In meinem Fall habe ich API Gateway verwendet und mein benutzerdefinierter Autor hat die Anfrage abgelehnt, aber den Access-Control-Allow-Origin Header nicht angehängt. Ich verwende serverlos, um Bereitstellungen zu verwalten, aber es kann in CloudFormation-Vorlagen konvertiert werden, wenn dies verwendet wird: https://serverless.com/blog/cors-api-gateway-survival-guide/#cors -with-custom-authorizers.

Schöner Fund @chris-feist!
Durch das Hinzufügen des Headers Access-Control-Allow-Origin zur Antwort wurde der Statuscode für apollo-client verfügbar

In express.js habe ich einfach Folgendes getan:

res.set('Access-Control-Allow-Origin', '*').status(401).send()

Lässt die Verwendung von Access-Control-Allow-Origin kein potenzielles Sicherheitsrisiko zu? https://stackoverflow.com/questions/12001269/what-are-the-security-risks-of-setting-access-control-allow-origin

Also für alle die noch suchen...

Der statusCode-Wert wird als Eigenschaft für networkError zurückgegeben, und die aktualisierte Typisierung für networkError wurde hier hinzugefügt: https://github.com/apollographql/apollo-link/pull/530

Da networkError jetzt ein Union-Typ von Error | ServerError | ServerParseError , erlaubt uns Typescript nur den Zugriff auf Eigenschaften, die allen Typen in der Union gemeinsam sind. statusCode existiert nicht auf Typ Error . Wir werden nicht über networkError.statusCode darauf zugreifen können, ohne dass sich Typescript beschwert; wir müssen einen Typenwächter oder etwas anderes verwenden, um die Typen zu unterscheiden.

Im Typescript-Handbuch werden mehrere Methoden vorgeschlagen: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types

Für meine Zwecke habe ich den Operator in , um die Dinge zum Laufen zu bringen.

  onError: ({ networkError }: ErrorResponse) => {
    if (
      networkError &&
      'statusCode' in networkError &&
      networkError.statusCode === 401
    ) {
      // perform logout stuff
    }
  },

@JoviDeCroock Denken

@mrkrli Danke. Anständige Lösung.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen