Apollo-link: networkError di apollo-link-error tidak memiliki atribut status

Dibuat pada 2 Des 2017  ·  35Komentar  ·  Sumber: apollographql/apollo-link

Sebagai subjek. Terima kasih

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

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

Komentar yang paling membantu

Jadi bagi yang masih mencari...

Nilai statusCode dikembalikan sebagai properti di networkError, dan pengetikan yang diperbarui untuk networkError ditambahkan di sini: https://github.com/apollographql/apollo-link/pull/530

Karena networkError sekarang merupakan tipe gabungan dari Error | ServerError | ServerParseError , TypeScript hanya akan mengizinkan kita mengakses properti yang umum untuk semua tipe dalam gabungan. statusCode tidak ada pada tipe Error . Kami tidak akan dapat mengaksesnya melalui networkError.statusCode tanpa keluhan TypeScript; kita perlu menggunakan pelindung tipe atau sesuatu yang lain untuk membedakan tipenya.

Beberapa metode disarankan dalam buku pegangan TypeScript: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types

Untuk tujuan saya, saya menggunakan operator in agar semuanya berfungsi.

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

@JoviDeCroock Pikirkan masalah ini aman untuk ditutup?

Semua 35 komentar

Seperti yang saya tahu "gagal mengambil" terjadi jika tidak ada respons (mis. server sedang down). Itu karena Anda tidak akan melihat kode status apa pun. Tolong koreksi saya jika saya salah.

Tapi saya pikir dalam situasi seperti itu bukan ide terbaik untuk hanya memiliki pesan teks - harus ada sesuatu yang dapat ditangani dengan lebih mudah (seperti kode kesalahan).

saya memang melihat permintaan dikirim dan masuk (401) di tag jaringan inspektur chrome.

Saya pikir ini terkait dengan #218 (tidak yakin..)

Juga memiliki masalah ini. networkError.status atau networkError.statusCode tidak ada dan objek respons tidak ada. Tidak ada cara untuk menangani kode status HTTP di lapisan jaringan.

Saya juga punya masalah ini saat ini. Itu seharusnya diperbaiki dalam rilis terbaru, tetapi saya masih mengalami masalah ini.

ada pemikiran tentang ini @jbaxleyiii ?

Saya menyesal mendengar bahwa Anda @akrigline dan @bogdansoare masih mengalami masalah dengan ini! Ini pasti harus diperbaiki.

Saya sedang mengerjakan #364 sekarang. Sementara itu, dapatkah Anda membuat atau memodifikasi kode sandbo ini untuk mereproduksi kesalahan? Atau bahkan lebih baik membuka PR dengan tes yang gagal?

ada berita?

Juga mengalami masalah ini, objek respons kosong dan tidak ada statusCode pada objek networkError.

Saya bahkan tidak mendapatkan properti networkError :
screen shot 2018-05-07 at 13 01 56

EDIT: Kemungkinan besar #475

Saya telah memperbaiki masalah ini dengan mengabaikan tipe Error , Tapi ini adalah solusi yang sepenuhnya sementara dan kami harus menunggu perbaikan di masa mendatang. @evans

Inilah masalah ini!

Arus networkError yang kami tulis tentang mereka bukanlah kesalahan sisi server yang sebenarnya. Singkatnya, kami tidak akan memiliki statusCode sama sekali dalam kasus ini dan pada kenyataannya, Kami perlu membuat atau menerima kesalahan jaringan nyata di server graphql. (_Jika Anda baru saja mengalami masalah komunikasi dengan server, Anda tidak dapat mengakses statusCode _).

screen shot 2018-05-07 at 20 38 09

Kesalahan jenis huruf masih tersisa!

Jika Anda menggunakan TypeScript, Anda dapat melihat bahwa networkError memiliki tipe berikut:

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

Dan Error didefinisikan sebagai:

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

Kita dapat melihat bahwa statusCode tidak didefinisikan di sini, Jadi kita tidak dapat memanggil sesuatu seperti networkError.statusCode di TypeScript.

Solusi sementara saya

Kita harus menggunakan networkError sebagai any dan juga kita membuatnya sebagai object kosong.

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

Menurut kode afterWareLink , kami menemukan kesalahan server dengan 400 kode status dan sekarang akan dapat mengakses kode status ini:

screen shot 2018-05-07 at 20 28 03

Inilah definisi ApolloClient saya:

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

Semoga bermanfaat.

Jika Anda menggunakan server simpul dan memiliki akses ke objek respons Anda dari konteks, Anda dapat menyelesaikannya dengan menambahkan; ctx.res.status(401); sebelum mengembalikan tanggapan Anda.
Sekarang apollo-link-errors dan apollo-link-retry keduanya akan dapat mengambil kesalahan dari objek networkErrors.

Berikut adalah contoh di mana saya menggunakan arahan skema khusus untuk memastikan pengguna diautentikasi;

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

Apakah sudah diperbaiki atau tidak? Saya masih tidak dapat mengakses kode status apa pun di objek networkError.

@artsiompeshko
Saya dapat mengaksesnya dengan cara ini di TypeScript:

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

Ada berita tentang ini? Permasalahan yang sama. networkError.statusCode tersedia dalam kode javascript saya.

@goldenbearkin mungkin kita salah menggunakannya dengan benar?

Kami harus mengizinkan kode status jaringan dalam kesalahan, saat ini tidak ada cara untuk mengakses status jaringan. Itu networkError sebenarnya adalah objek tipe HttpErrorResponse yang memiliki properti status yang selalu 0 mana kode status asli di konsol adalah 401 atau 403

@lvlohammadi
Bisakah solusi yang sama dilakukan untuk graphQLErrors juga? Saya dapat melihat kode status untuk networkErrors menggunakan solusi Anda - menguji ini dengan 504. Tetapi ketika otentikasi gagal, saya mengharapkan kode status graphQLError 401. Tetapi tidak ada cara untuk mengaksesnya saat ini. Saya mencoba opsi 'graphQLErrors = {} as any', tetapi tidak berhasil.

Ini aneh, tetapi saya sebenarnya memiliki properti statusCode di networkError . Namun, tidak ada properti seperti itu di antarmuka, jadi TS mengeluarkan kesalahan di sini.

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

Di server saya menggunakan koa dan koa-passport , dan kode yang sesuai di sini adalah:

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

Karena masalah ini benar-benar usang, saya akan menutupnya tetapi jika Anda masih khawatir tentang ini, silakan buka kembali dan saya akan segera menghubungi Anda kembali.

@JoviDeCroock menghadapi masalah itu sekarang... jadi, apa solusinya? Bagaimana cara mendapatkan status kesalahan dari networkError?

Baiklah, jadi Anda yakin itu adalah networkError (tidak ada graphqlError?) dan statusCode/status tidak ditentukan?

Apakah ini kesalahan TypeScript atau benar-benar tidak terdefinisi? Bisakah Anda mereproduksinya?

Btw downvoting komentar saya tidak membantu menyelesaikan kesalahan ini, tidak pernah ada cara untuk masalah ini direproduksi ditambah masalah yang perlu dipangkas untuk meningkatkan cara kami dapat membantu.

Saya memiliki masalah yang sama, namun bisa jadi karena cara saya mengirim status dari server. Saya pada dasarnya mencoba untuk pintas salah satu penanganan khusus graphQL dan hanya menanggapi dengan, misalnya:
res.status(404).send("The resource you are looking for cannot be found")

Namun saya hanya mendapatkan TypeError: Failed To Fetch . Apakah ini karena detail implementasi di Apollo-Server-Express?

@JoviDeCroock Sebenarnya, saya sedikit kacau dengan kode graphql ketika mencoba menerapkan penanganan kesalahan sisi klien untuk 401 atau 403. Kami menggunakan middleware, dan yang saya lakukan hanyalah mengembalikan selalu 401 untuk setiap permintaan. Dengan begitu juga permintaan OPTION mendapat 401, apa yang seharusnya tidak terjadi, dan mungkin karena itu saya tidak dapat melihat status sama sekali. Setelah memperbarui kode, saya dapat melihat kode status

Terima kasih atas pembaruannya @dimitriy-k senang mendengar bahwa masalah ini telah teratasi.

Untuk @AlbertoPL , saya akan memeriksa masalah Anda malam ini. Jangan ragu untuk memberikan lebih banyak komentar tentang hal itu jika Anda bisa.

@JoviDeCroock Terima kasih telah melihat. Jadi kode spesifik yang saya coba untuk mulai bekerja adalah sebagai berikut:
res.status(426).send()

Di tab jaringan Chrome, saya melihat bahwa titik akhir /graphql merespons dengan kode status 426 dan respons yang sesuai (Diperlukan Peningkatan Versi). Namun saya masih hanya melihat TypeError: Failed to Fetch Saat menggunakan tautan onError dari apollo-link-error . Jika saya bisa mendapatkan kode status yang sebenarnya, itu akan bagus (atau bahkan pesannya sudah cukup).

Baiklah, itu beberapa informasi yang cukup bagus. Kami harus dapat men-debug ini entah bagaimana, apakah Anda setuju dengan ini atau?

Ya saya pasti bisa mencoba. Saya sangat menghargai bantuan/tips untuk melakukannya.

Jadi hanya dengan menelusuri kode di debugger Chrome, saya dapat melihat bahwa apollo-link-batch-http dan apollo-link-http melakukan panggilan API menggunakan fungsi fetcher (baik diteruskan sebagai opsi tautan atau default, saya mencoba keduanya):

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

Itu tidak pernah masuk ke salah satu blok then, melainkan masuk ke blok catch. Pada saat itu kesalahan sudah diatur ke "Gagal Mengambil". Tidak ada kode status.

Kami harus memeriksa apakah itu salah di sini:

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

Saya akan mencoba membuat reproduksi akhir pekan ini, memiliki jadwal yang cukup padat di tempat kerja.

EDIT:

Masalah utamanya adalah setiap kali saya menguji ini, saya menggunakan apollo-server dan itu berjalan dengan baik tetapi bisa jadi ikatannya atau sesuatu yang sedikit berbeda. Jadi sulit bagi saya untuk menguji ini tanpa server sebagai reproduksi

Jika itu akan membantu, inilah server Apollo kami (perhatikan bahwa kami menggunakan Apollo-server-express). CreateGraphQLSchema mengembalikan instance Apollo Server yang kami gunakan:

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 Saya juga mengalami masalah ini. Ternyata ini masalah CORS , bukan klien Apollo.

Janji fetch() akan ditolak dengan TypeError ketika terjadi kesalahan jaringan atau CORS salah dikonfigurasi di sisi server

Pastikan Anda mengembalikan tajuk yang benar saat menolak permintaan. Dalam kasus saya, saya menggunakan API Gateway dan otorisasi kustom saya menolak permintaan, tetapi tidak melampirkan header Access-Control-Allow-Origin . Saya menggunakan tanpa server untuk mengelola penerapan, tetapi dapat dikonversi ke templat CloudFormation jika itu yang digunakan: https://serverless.com/blog/cors-api-gateway-survival-guide/#cors -with-custom-authorizers.

Temuan yang bagus @chris-feist!
Menambahkan header Access-Control-Allow-Origin ke respons membuat kode status tersedia untuk apollo-client

Di express.js, saya cukup melakukan hal berikut:

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

Tidakkah menggunakan Access-Control-Allow-Origin memungkinkan potensi risiko keamanan? https://stackoverflow.com/questions/12001269/what-are-the-security-risks-of-setting-access-control-allow-origin

Jadi bagi yang masih mencari...

Nilai statusCode dikembalikan sebagai properti di networkError, dan pengetikan yang diperbarui untuk networkError ditambahkan di sini: https://github.com/apollographql/apollo-link/pull/530

Karena networkError sekarang merupakan tipe gabungan dari Error | ServerError | ServerParseError , TypeScript hanya akan mengizinkan kita mengakses properti yang umum untuk semua tipe dalam gabungan. statusCode tidak ada pada tipe Error . Kami tidak akan dapat mengaksesnya melalui networkError.statusCode tanpa keluhan TypeScript; kita perlu menggunakan pelindung tipe atau sesuatu yang lain untuk membedakan tipenya.

Beberapa metode disarankan dalam buku pegangan TypeScript: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types

Untuk tujuan saya, saya menggunakan operator in agar semuanya berfungsi.

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

@JoviDeCroock Pikirkan masalah ini aman untuk ditutup?

@mrkrlli Terima kasih. Solusi yang layak.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat