Apollo-link: خطأ في الشبكة في خطأ apollo-link ليس له سمة الحالة

تم إنشاؤها على ٢ ديسمبر ٢٠١٧  ·  35تعليقات  ·  مصدر: apollographql/apollo-link

كموضوع. شكرا

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

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

التعليق الأكثر فائدة

لذلك لأي شخص لا يزال يبحث ...

يتم إرجاع قيمة statusCode كخاصية على networkError ، وتمت إضافة الكتابة المحدثة لـ networkError هنا: https://github.com/apollographql/apollo-link/pull/530

نظرًا لأن networkError أصبح الآن نوعًا من النقابات Error | ServerError | ServerParseError ، فإن Typescript لن تسمح لنا إلا بالوصول إلى الخصائص المشتركة لجميع الأنواع في الاتحاد. statusCode غير موجود على النوع Error . لن نتمكن من الوصول إليه عبر networkError.statusCode بدون شكوى Typescript ؛ نحتاج إلى استخدام نوع guard أو أي شيء آخر للتمييز بين الأنواع.

تم اقتراح عدة طرق في كتيب Typescript: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types

لأغراضي ، استخدمت عامل التشغيل in لتشغيل الأشياء.

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

JoviDeCroock هل تعتقد أن إغلاق هذه المشكلة آمن؟

ال 35 كومينتر

كما أعلم ، يحدث "فشل في الجلب" إذا لم يكن هناك استجابة (مثل تعطل الخادم). هذا لأنك لن ترى أي رمز حالة. يرجى تصحيح لي إذا كنت مخطئا.

لكنني أعتقد أنه في مثل هذه المواقف ، ليس من الأفضل أن يكون لديك رسالة نصية فقط - يجب أن يكون هناك شيء يمكن التعامل معه بسهولة أكبر (مثل رمز الخطأ).

لقد رأيت طلبًا تم إرساله وفي (401) في علامة الشبكة لمفتش الكروم.

أعتقد أن هذا مرتبط بـ # 218 (لست متأكدًا ..)

أيضا وجود هذه المشكلة. networkError.status أو networkError.statusCode مفقود وكائن الاستجابة مفقود. لا توجد طريقة للتعامل مع أكواد حالة HTTP في طبقة الشبكة.

لدي أيضًا هذه المشكلة حاليًا. من المفترض أنه تم إصلاحه في أحدث إصدار ، لكن ما زلت أواجه هذه المشكلة.

أي أفكار حول هذا jbaxleyiii ؟

يؤسفني سماع أن akrigline و bogdansoare ما

أنا أعمل من خلال # 364 الآن. في غضون ذلك ، هل يمكنك إنشاء أو تعديل وضع الحماية للشفرة لإعادة إنتاج الخطأ؟ أو حتى من الأفضل فتح العلاقات العامة باختبار فاشل؟

أى اخبار؟

مع وجود هذه المشكلة أيضًا ، يكون كائن الاستجابة فارغًا ولا يوجد رمز حالة على كائن networkError.

لا أحصل حتى على خاصية networkError :
screen shot 2018-05-07 at 13 01 56

تحرير: على الأرجح # 475

لقد أصلحت هذه المشكلة من خلال تجاهل النوع Error ، لكن هذا حل مؤقت تمامًا وعلينا انتظار الإصلاحات المستقبلية. @ إيفانز

ها هي هذه المشكلة!

لم يكن تيار networkError الذي كتبناه عنهم خطأً حقيقيًا من جانب الخادم. باختصار ، لن يكون لدينا statusCode على الإطلاق في هذه الحالات ، وفي الواقع ، نحتاج إلى إنشاء أو تلقي خطأ شبكة حقيقي على خادم Graphql. (_ إذا كانت لديك مشكلة في الاتصال بالخادم فقط ، فلا يمكنك الوصول إلى statusCode _).

screen shot 2018-05-07 at 20 38 09

خطأ محرف لا يزال باقيا!

إذا كنت تستخدم TypeScript ، فيمكنك ملاحظة أن networkError يحتوي على النوع التالي:

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

ويتم تعريف Error على النحو التالي:

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

يمكننا أن نرى أن statusCode لم يتم تعريفه هنا ، لذلك لا يمكننا استدعاء شيء مثل networkError.statusCode في TypeScript.

الحل المؤقت الخاص بي

يجب أن نظهر خطأ الشبكة كـ any ونجعله فارغًا 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}`);
});

وفقًا لرموز afterWareLink أعلاه ، واجهنا خطأ في الخادم برمز الحالة 400 وسنتمكن الآن من الوصول إلى رمز الحالة هذا:

screen shot 2018-05-07 at 20 28 03

هذا هو تعريف ApolloClient الخاص بي:

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

نأمل أن تكون مفيدة.

إذا كنت تستخدم خادم عقدة ولديك حق الوصول إلى كائن الاستجابة الخاص بك من السياق ، فيمكنك حل ذلك عن طريق إضافة ؛ ctx.res.status(401); قبل أن يعود ردك.
الآن سيتمكن كل من apollo-link-errors و apollo-link-retry من التقاط الخطأ من كائن networkErrors.

فيما يلي مثال حيث أستخدم توجيه مخطط مخصصًا للتأكد من مصادقة المستخدم ؛

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

هل تم إصلاحه أم لا؟ ما زلت لا أستطيع الوصول إلى أي رمز حالة في كائن networkError.

تضمين التغريدة
يمكنني الوصول إليه بهذه الطريقة في 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)
})

أي أخبار عن هذا؟ نفس المشكلة. لا يتوفر networkError.statusCode في كود جافا سكريبت الخاص بي.

goldenbearkin ربما أخطأنا في استخدامها بشكل صحيح؟

يجب أن نسمح برمز حالة الشبكة في الخطأ ، لا توجد حاليًا طريقة للوصول إلى حالة الشبكة. إنه networkError هو في الواقع كائن من النوع HttpErrorResponse له خاصية status والتي تكون دائمًا 0 حيث رمز الحالة الأصلي في وحدة التحكم هو 401 أو 403

تضمين التغريدة
هل يمكن إجراء نفس الحل البديل بالنسبة إلى الرسوم البيانية QLErrors أيضًا؟ يمكنني رؤية رمز الحالة لأخطاء الشبكة باستخدام الحل البديل الخاص بك - اختبر هذا مع 504. ولكن عندما تفشل المصادقة ، أتوقع رمز حالة خطأ في الرسم البياني QLE 401. ولكن لا توجد طريقة للوصول إليه حاليًا. لقد جربت الخيار "graphQLErrors = {} كأي خيار" ، ولكن دون جدوى.

هذا غريب ، لكن لدي بالفعل خاصية statusCode على networkError . ومع ذلك ، لا توجد مثل هذه الخاصية في الواجهة ، لذلك ينتج خطأ TS هنا.

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

أستخدم على الخادم koa و koa-passport ، والجزء المقابل من الكود هنا هو:

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

نظرًا لأن هذه المشكلة قديمة حقًا ، فأنا أقوم بإغلاقها ولكن إذا كنت لا تزال قلقًا بشأن هذا الأمر ، فلا تتردد في إعادة فتحه وسأعود إليك في أسرع وقت ممكن.

JoviDeCroock الذي يواجه هذه المشكلة الآن ... فما الحل؟ كيف أحصل على حالة الخطأ من NetworkError؟

حسنًا ، أنت متأكد من أنه خطأ في الشبكة (لا يوجد خطأ في الرسم البياني؟) وأن statusCode / status غير محدد؟

هل هو خطأ مطبعي أم غير محدد حقًا؟ هل يمكنك إعادة إنتاجه؟

بالمناسبة ، فإن التصويت المنخفض لتعليقي لا يساعد في حل هذا الخطأ ، ولم يكن هناك أي طريقة لإعادة إنتاج هذه المشكلة بالإضافة إلى المشكلات التي يجب قطعها لتحسين الطريقة التي يمكننا بها المساعدة.

لدي نفس المشكلة ، ولكن قد يكون ذلك بسبب الطريقة التي أرسل بها الحالة من الخادم. أحاول بشكل أساسي اختصار أي معالجة خاصة بـ GraphQL والاستجابة ببساطة ، على سبيل المثال:
res.status(404).send("The resource you are looking for cannot be found")

ومع ذلك ، أحصل فقط على TypeError: Failed To Fetch . هل هذا بسبب تفاصيل التنفيذ في Apollo-Server-Express؟

JoviDeCroock في الواقع ، لقد أخطأت قليلاً في كود Graphql عند محاولة تنفيذ معالجة خطأ من جانب العميل لـ 401 أو 403. نحن نستخدم برمجيات وسيطة ، وما فعلته هو إعادة 401 دائمًا لكل طلب. بهذه الطريقة أيضًا حصل طلب OPTION على 401 ، ما لا يجب أن يحدث ، وربما بسبب ذلك لم أتمكن من رؤية الحالة على الإطلاق. بعد تحديث الرمز ، يمكنني رؤية رموز الحالة

نشكرك على التحديث @ dimitriy-k سعيد لسماع أن هذه المشكلة قد تم حلها.

بالنسبة إلى AlbertoPL ،

JoviDeCroock شكرا لإلقاء نظرة. لذا فإن الكود المحدد الذي أحاول الحصول عليه هو ما يلي:
res.status(426).send()

في علامة تبويب الشبكة في Chrome ، أرى أن نقطة النهاية / graphql تستجيب برمز حالة 426 واستجابة مناسبة (تتطلب الترقية). ومع ذلك ، ما زلت أرى فقط TypeError: Failed to Fetch عند استخدام رابط onError من apollo-link-error . إذا كان بإمكاني الحصول على رمز الحالة الحقيقي فسيكون ذلك رائعًا (أو حتى الرسالة ستكون كافية).

حسنًا ، هذه بعض المعلومات الرائعة جدًا. يجب أن نكون قادرين على تصحيح هذا بطريقة ما ، هل تشعر أنك مستعد لهذا أم؟

نعم يمكنني بالتأكيد المحاولة. أقدر أي مساعدة / نصائح للقيام بذلك.

لذا بمجرد التنقل عبر التعليمات البرمجية في مصحح أخطاء Chrome ، يمكنني رؤية أن كلاً من apollo-link-batch-http و apollo-link-http يجرون مكالمات API باستخدام وظيفة الجلب (إما أن يتم تمريرها كخيار ارتباط أو بشكل افتراضي ، لقد جربت كليهما):

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

لا تدخل أبدًا في أي من الكتل التي تم إنشاؤها بعد ذلك ، ولكنها بدلاً من ذلك تدخل في كتلة الالتقاط. بحلول ذلك الوقت ، تم تعيين الخطأ بالفعل على "فشل الجلب". لا يوجد رمز الحالة موجود.

يجب أن نتحقق مما إذا حدث خطأ هنا:

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

سأحاول إجراء استنساخ في نهاية هذا الأسبوع ، مع وجود جدول أعمال مزدحم جدًا في العمل.

تعديل:

المشكلة الرئيسية هي أنه في كل مرة أختبر فيها هذا ، أستخدم apollo-server وسارت الأمور على ما يرام ولكن قد تكون الروابط أو شيئًا مختلفًا بعض الشيء. لذلك من الصعب بالنسبة لي اختبار هذا بدون خادم كنسخ

إذا كان ذلك سيساعد ، فإليك خادم Apollo الخاص بنا (لاحظ أننا نستخدم Apollo-server-Express). يقوم CreateGraphQLSchema بإرجاع مثيل خادم Apollo الذي نستخدمه:

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 واجهت هذه المشكلة أيضًا. اتضح أنها مشكلة CORS ، وليست عميل Apollo.

سيتم رفض وعد الجلب () مع وجود خطأ في النوع عند مواجهة خطأ في الشبكة أو تكوين CORS بشكل خاطئ على جانب الخادم

تأكد من إعادة العناوين الصحيحة عند رفض الطلب. في حالتي ، كنت أستخدم بوابة API وكان المفوض المخصص الخاص بي يرفض الطلب ، ولكن لم يقم بإرفاق رأس Access-Control-Allow-Origin . أستخدم بدون خادم لإدارة عمليات النشر ، ولكن يمكن تحويلها إلى قوالب CloudFormation إذا كان هذا هو ما يتم استخدامه: https://serverless.com/blog/cors-api-gateway-survival-guide/#cors -with-custom-authorizers.

لطيفة تجد @ كريس فيست!
أدت إضافة رأس Access-Control-Allow-Origin إلى الاستجابة إلى إتاحة رمز الحالة لبرنامج Apollo-client

في express.js ، قمت بما يلي ببساطة:

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

ألا يسمح استخدام Access-Control-Allow-Origin بمخاطر أمنية محتملة؟ https://stackoverflow.com/questions/12001269/what-are-the-security-risks-of-setting-access-control-allow-origin

لذلك لأي شخص لا يزال يبحث ...

يتم إرجاع قيمة statusCode كخاصية على networkError ، وتمت إضافة الكتابة المحدثة لـ networkError هنا: https://github.com/apollographql/apollo-link/pull/530

نظرًا لأن networkError أصبح الآن نوعًا من النقابات Error | ServerError | ServerParseError ، فإن Typescript لن تسمح لنا إلا بالوصول إلى الخصائص المشتركة لجميع الأنواع في الاتحاد. statusCode غير موجود على النوع Error . لن نتمكن من الوصول إليه عبر networkError.statusCode بدون شكوى Typescript ؛ نحتاج إلى استخدام نوع guard أو أي شيء آخر للتمييز بين الأنواع.

تم اقتراح عدة طرق في كتيب Typescript: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types

لأغراضي ، استخدمت عامل التشغيل in لتشغيل الأشياء.

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

JoviDeCroock هل تعتقد أن إغلاق هذه المشكلة آمن؟

mrkrlli شكرا. حل لائق.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات