Apollo-link: حقل الاستجابة غير محدد لـ networkError [apollo-link-error]

تم إنشاؤها على ٦ نوفمبر ٢٠١٨  ·  29تعليقات  ·  مصدر: apollographql/apollo-link


يبدو أن هناك خطأ في وظيفة onError أو في وثائقها. تم ذكر المشكلة بالفعل في # 698 لكنني اعتقدت أنني سأقوم بتجسيدها قليلاً على أمل التوصل إلى حل.

عند اعتراض خطأ باستخدام onError إما من apollo-boost أو مباشرة من apollo-link-error ، تشير الوثائق إلى أن كائن الخطأ يحتوي على كائن response ، والذي يمكن استخدامه تجاهل الأخطاء.

لقد قمت بتمييز مربع اختيار المستندات نظرًا لأن هذا قد يكون سلوكًا مقصودًا ، ولكن كما تشير المستندات إلى الكائن response في فقرة فرعية من القسم networkError ، فمن المحتمل أن يتم توضيحه قليلاً إذا كان هذا صحيحًا القضية.

سلوك متوقع
يجب أن يمنع تعيين response.errors = null; انتشار الخطأ دون حدوث خطأ آخر.

السلوك الفعلي
عند التقاط networkError ، يكون الحقل response غير محدد ، مما يتسبب في حدوث أخطاء في بناء الجملة عند محاولة تعيين response.errors . لأسباب خارجة عني في الوقت الحالي ، يمنع هذا في الواقع خطأ الشبكة من الانتشار ، ولكنه يلقي بخطأ آخر بدلاً من ذلك.

في الإعداد الخاص بي (MacOS 10.14.1 w. Chrome 70.0.3538.77) ، سيؤدي تعيين response.errors = null إلى الحصول على Uncaught TypeError: Cannot set property 'errors' of undefined و response = { errors: null } سيؤدي إلى Uncaught Error: "response" is read-only .

skaermbillede 2018-11-06 kl 15 08 10

skaermbillede 2018-11-06 kl 15 09 20

استنساخ بسيط

ما يلي هو إعادة إنتاج باستخدام apollo-boost . تحدث نفس المشكلة باستخدام 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 
    }
  })
});

تسميات العدد

  • [] وقد التكاثر
  • [ ] خاصية
  • [x] مستندات
  • [] الحجب
  • [] مشكلة أولى جيدة

docs

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

أي تقدم في هذه المسألة؟

ال 29 كومينتر

هل هناك أي حل مؤقت لهذا الخطأ؟

إجراء 1+ يمثل مشكلة كبيرة جدًا في تصحيح الأخطاء إذا كنت تستخدم عميل Apollo في بيئة الخادم (قراءة البيانات من خادم GraphQL مختلف لمحللاتك).

+1
هل هناك أي حل بديل لتجاهل الأخطاء بشكل مشروط؟

ولدي أيضا هذه المسألة.

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

لقد بحثت في هذا ووفقًا للمستندات ، يجب الرد على رد الاتصال:
https://www.apollographql.com/docs/link/links/error.html#callback

يحدث هذا مع رد الاتصال العادي: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L46

ولكن مع خطأ الشبكة: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L62

أي فكرة عن موعد دمجها؟ :)

ربما قريبًا جدًا ، يركز فريق أبولو على إصدار العميل الجديد وإصدار رد الفعل.

أي تقدم في هذه المسألة؟

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

كنت أيضا أواجه نفس المشكلة. كان الحل البديل بالنسبة لي هو إرجاع Observable فارغًا.

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

import { Observable } from 'apollo-link';

...

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

...

@ luki215 ليس لدي أي حقل رمز خطأ
image

Sceat الذي apollo-link تستخدمه؟ أستخدم apollo-angular-link-http ، لذلك قد يكون هذا هو الاختلاف.

@ luki215 لقد كان خطأي حقًا ، باستخدام بوابة AWS api لم أقم بتكوين استجابة CORS بشكل صحيح لأي شيء آخر رموزه 200

بالنسبة للأشخاص الذين يواجهون المشكلة ، يمكنك إضافة ردود بوابة api في 4xx الافتراضي:
image

أو في 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'

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

strass dunno حول حقل الاستجابة هذا ولكن للتعامل مع الأخطاء ، يمكنك تشغيل خطأ في الشبكة

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

لقد جربت للتو حل @ luki215 لإعادة ملف Observable فارغ ولكن في حالتي ، هذا سيجعل رد فعل أبولو يرمي استثناءً:

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

أنا أستخدم هذا لتغيير الخطأ الذي تم إرجاعه إلى المكون باستخدام Mutation

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

أعتقد أن الشيء الرئيسي الذي ربما يبحث عنه الأشخاص هنا هو القدرة على تجاوز سلسلة الخطأ التي تم إرجاعها في networkError ويتم ذلك باستخدام networkError.message = 'Some custom error message.'

توصل إلى حل بديل بعد البحث في apollo-link-rest وهذه الحزمة. بدلاً من استخدام onError ، أعدت كتابة خاصتي (أدناه).

النية: اعترض NetworkError وقم بتحويله بشروط إلى GraphQLError . السبب هو أنني أستخدم نقطة نهاية REST لا تتبع اصطلاح GraphQL ، وتقوم بإرجاع أشكال مثل {"message": "{ \"key\": \"not valid\"}"} برمز الحالة 400. لا ينبغي التعامل مع ذلك على أنه خطأ في الشبكة.

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

ما الذي لا يعمل:

  • أفضل استخدام observer.next({ data: { ... }, errors: { ... } }) لتمرير الأخطاء بالطريقة الصحيحة ، لكنها لا تعمل (هذه البيانات لا تذهب إلى أي مكان ، ولا يبدو أن الوعد بالطفرة قد تم حله).
  • استدعاء observer.next() و observer.error() بالتسلسل ليس له السلوك الذي كنت أتوقعه ، بعد هذا . يبدو أن استدعاء الخطأ فقط ساري المفعول.
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();
      };
    });
  });

يبدو لي أن هذا يجب أن يعمل ، ولكن لا يبدو أن وظيفة mutate() تحل أو ترفض - لا يتم استدعاء then() ولا catch() .

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

فقط ادفع ...
يتعثر في نفس الخطأ الآن.
تريد إعادة تعيين الخطأ في حقل الاستجابة لمعالجة الخطأ في المكون نفسه ...

أي تقدم؟

+1 على نفس المشكلة.

أعتقد أنني قد وجدت حلاً (على الأقل لمشكلة انتشار الخطأ):

استبدال map بـ forEach في errorLink يعني أن الأخطاء يمكن أن تنشر الأخطاء إلى واجهة المستخدم (كان الخطأ الناتج سابقًا في مكون واجهة المستخدم غير محدد ، والآن يحتوي على الخطأ (الأخطاء)):

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

كنت أيضا أواجه نفس المشكلة. كان الحل البديل بالنسبة لي هو إرجاع Observable فارغًا.

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

import { Observable } from 'apollo-link';

...

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

...

لقد نجح هذا الأمر بالنسبة لي ، لكنه يعمل تمامًا عكس ما تقوله الوثائق ، على سبيل المثال ، "يمكن لرد نداء الخطأ بشكل اختياري أن يعيد أمرًا يمكن ملاحظته من استدعاء الأمام (العملية) إذا أراد إعادة محاولة الطلب. ولا ينبغي إعادة أي شيء آخر."

بعد تحديث Apollo اليوم ، لم يعد الحل الذي قدمته أعلاه يحمل أخطاء GraphQL من خلال ربط أبولو بواجهة المستخدم.

هل لدى أي شخص حل آخر؟

strass ، هل يمكنك توضيح سبب إصلاح الحل أعلاه للمشكلة. IMO يفعلون نفس الشيء تمامًا باستخدام forEach و map .

لم يعد الأمر كذلك (انظر https://github.com/apollographql/apollo-link/issues/855#issuecomment-538010335)

أي تقدم في هذا؟

كيف يُقصد بنا التعامل مع حالة يقوم فيها الخادم بإرجاع الرقم 302 برأس "الموقع" لإعادة المصادقة ، إذا لم نتمكن من الحصول على كائن الاستجابة على الإطلاق؟

إذا كان لدى شخص ما أي دليل ، فأخبرني لأننا نفكر في إسقاط أبولو :(

ساعدني هذا بشكل كبير https://github.com/apollographql/apollo-link/issues/297#issuecomment -350488527 ويسمح بقراءة استجابات الشبكة.

شيء مهم عندما تكون هناك استجابات غير مصرح بها يتم إرجاعها من طبقات المصادقة / واجهات برمجة تطبيقات البوابة.

أرغب في رؤية هذا كجزء من الوظائف الأساسية!

أي تقدم في هذا؟ لا توجد طريقة هذا هو السلوك المقصود. وإلا كيف يمكن إعادة توجيه أخطاء GraphQL إلى العميل برمز حالة غير 200؟

مجرد ملاحظة لأي شخص آخر يواجه مشكلة في اكتشاف أخطاء الشبكة وتعديلها. كنت أحاول إلغاء تغليف أخطاء الشبكة بالخطأ الداخلي من الرسم البياني وإليك ما انتهى بي الأمر:

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

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

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