Apollo-link: Поле ответа не определено для networkError [apollo-link-error]

Созданный на 6 нояб. 2018  ·  29Комментарии  ·  Источник: apollographql/apollo-link


Кажется, есть ошибка либо в функции onError , либо в ее документации. Эта проблема уже упоминалась в # 698, но я подумал, что немного конкретизирую ее в надежде на решение.

При перехвате ошибки с помощью onError либо из apollo-boost , либо напрямую из apollo-link-error в документации указано , что объект ошибки содержит объект response , который можно использовать для игнорировать ошибки.

Я отметил флажок docs , так как это может быть предполагаемым поведением, но поскольку в документах упоминается объект response в подпункте раздела networkError , вероятно, следует немного уточнить, если это так. дело.

Ожидаемое поведение
Установка response.errors = null; должна предотвратить распространение ошибки без возникновения другой ошибки.

Фактическое поведение
При перехвате networkError поле response не определено, что вызывает синтаксические ошибки при попытке установить response.errors . По причинам, которые на данный момент мне не известны, это фактически предотвращает распространение сетевой ошибки, но вместо этого выдает другую ошибку.

В моей настройке (MacOS 10.14.1 с 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 
    }
  })
});

Ярлыки выпуска

  • [ ] has-воспроизведение
  • [ ] характерная черта
  • [х] документы
  • [ ] блокировка
  • [ ] хороший первый выпуск

Самый полезный комментарий

Есть ли прогресс в этом вопросе?

Все 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 .

Но с networkError: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L62

есть информация о том, когда он будет объединен? :)

Вероятно, совсем скоро команда Apollo сосредоточится на выпуске нового клиента и реактивной версии.

Есть ли прогресс в этом вопросе?

Мне тоже интересно, есть ли прогресс в этом? Или временное решение? Я хотел бы иметь способ распространять пользовательские сообщения об ошибках из API в пользовательский интерфейс.

Я также столкнулся с той же проблемой. Обходной путь для меня состоял в том, чтобы вернуть пустой 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 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 не знает об этом поле ответа, но для обработки ошибок вы можете включить сетевую ошибку

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, но в моем случае это теперь заставит react-apollo генерировать исключение:

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. Это не следует рассматривать как NetworkError.

Что работает: это решение действительно передает фактическое сообщение об ошибке из ответа вниз по цепочке таким образом, что я могу получить и проанализировать его в обещании $ 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 означает, что ошибки могут передавать ошибки в пользовательский интерфейс (ранее результат.error в компоненте пользовательского интерфейса был неопределенным, теперь он содержит ошибки):

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 через хуки apollo в пользовательский интерфейс.

У кого-нибудь есть другое решение?

@strass , можете ли вы объяснить, почему ваше решение, приведенное выше , устраняет проблему. ИМО они делают то же самое, используя forEach и map .

Больше нет (см. https://github.com/apollographql/apollo-link/issues/855#issuecomment-538010335)

Есть ли прогресс в этом?

Как мы должны поступать в случае, когда сервер возвращает 302 с заголовком «Местоположение» для повторной аутентификации, если мы вообще не можем получить объект ответа?

Если у кого-то есть какие-либо подсказки, дайте мне знать, потому что мы думаем сбросить Аполлона :(

Это очень помогло мне https://github.com/apollographql/apollo-link/issues/297#issuecomment -350488527 и позволяет читать сетевые ответы.

Что-то, что имеет решающее значение, когда есть несанкционированные ответы, возвращаемые от API-интерфейсов уровней аутентификации/шлюза.

Я хотел бы видеть это как часть основного функционала!

Есть ли прогресс в этом? Это никоим образом не является предполагаемым поведением. Как еще можно пересылать ошибки GraphQL клиенту с кодом состояния, отличным от 200?

Просто примечание для всех, кто сталкивается с проблемой перехвата и изменения сетевых ошибок. Я пытался преобразовать сетевые ошибки во внутреннюю ошибку из graphql, и вот что у меня получилось:

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 рейтинги