Apollo-link-rest: Ошибка, когда ответ REST пуст

Созданный на 7 мая 2018  ·  18Комментарии  ·  Источник: apollographql/apollo-link-rest


У меня есть запрос на изменение, который в случае успеха отвечает статусом 204 «Нет содержимого». В сочетании с ошибкой apollo-link-error я постоянно получаю сетевую ошибку: Неожиданный конец ввода JSON .

Похоже, что apollo-link-rest пытается проанализировать пустое тело ответа.

Есть идеи, как это смягчить? Кстати, я пытаюсь поговорить с API SharePoint REST, поэтому я не могу настроить ответ сервера.

Должен быть способ сообщить apollo-link-rest, как поступать с пустым ответом ...

Мой вызов мутации:

                  <Mutation mutation={M_WRITE_PROJECT_DETAILS}>
                    {(writeProjectDetails, { data }) => (
                      <Form.Button
                        content="Save"
                        onClick={() => {
                          const token = localStorage.getItem("token");
                          writeProjectDetails({
                            variables: {
                              projectId: project.Id,
                              input: {
                                __metadata: {
                                  type: "SP.Data.ProjectListItem"
                                },
                                Title: project.Title
                              }
                            },
                            context: {
                              headers: {
                                "X-RequestDigest": token,
                                "X-HTTP-Method": "MERGE",
                                "IF-MATCH": "*"
                              }
                            }
                          });
                        }}
                      />
                    )}
                  </Mutation>

Соответствующий запрос gql:

const M_WRITE_PROJECT_DETAILS = gql`
  mutation writeToSPList($projectId: String, $input: String) {
    writeToSPList(projectId: $projectId, input: $input)
      @rest(
        type: "Project"
        path: "/web/lists/GetByTitle('Projects')/items(:projectId)"
        method: "POST"
      ) {
      NoResponse
    }
  }
`;

"NoResponse", очевидно, _null_, поскольку нет ответа, но опять же, я не могу отправить мутацию без каких-либо полей ответа ... если я что-то не упускаю.

enhancement💡 feature help wanted 🛠 question❔

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

Я думаю, чтобы вышестоящая интерпретация пустого тела GraphQL работала, мы должны вернуть {} что затем приведет к

data: {
  __typename: "...",
  NoResponse: null,
}

Когда вы говорите: «Можно ли поддержать оба поведения?» Вы имеете в виду, глядя на код состояния 204 и глядя на заголовки? Я предполагаю, что когда на самом деле присутствует Content-Length , а также 0 тогда мы могли бы принять это как еще одно указание на пустое тело и вернуть значение по умолчанию.

Хотя я ранее предлагал заголовок Content-Type самом деле я не совсем понимаю, что делать, если для него установлено что-то иное, чем json . Я думаю, что для интерпретации этого заголовка, вероятно, потребуется использовать параметр конфигурации responseSerializer аналогичный предложенному bodySerializer . Это, вероятно, излишество и не решило бы, в частности, проблему пустого тела.

Я счастлив сделать реализацию 204 и Content-Length.

Все 18 Комментарий

@isopterix - Я не думаю, что сейчас у нас действительно есть простой способ справиться с этим. - Моя первоначальная мысль заключалась в том, чтобы порекомендовать вам обернуть Fetch и предоставить настраиваемую реализацию Fetch, которая заменяет тело из 204 ответов на {} - Я думаю, без создания лучшей функции, это единственный способ сделать это Cегодня!

@isopterix Я попытался воспроизвести эту проблему с помощью теста ниже, но тест прошел для пустого тела. Похоже, что fetch response.json() возвращает {} когда тело является пустой строкой. Не могли бы вы попытаться более внимательно отследить это в своей живой настройке, чтобы увидеть, где возникает ошибка?

  describe('response parsing', () => {
    it('supports empty response bodies', async () => {
      expect.assertions(1);

      const link = new RestLink({ uri: '/api' });

      fetchMock.post('/api/posts', {
        status: 204,
        body: '',
      });

      const mutation = gql`
        mutation postEmptyResponse($input: String!) {
          newPost(input: $input)
            @rest(type: "Post", path: "/posts", method: "POST") {
            NoResponse
          }
        }
      `;
      const {data} = await makePromise<Result>(
        execute(link, {
          operationName: 'postEmptyResponse',
          query: mutation,
          variables: { input: 'Love apollo' },
        }),
      );

      expect(data).toEqual({
        newPost: {
          NoResponse: null,
          __typename: 'Post',
        }
      });
    });
  });

@isopterix, не

Одна проблема, которую я вижу при обнаружении пустого тела, заключается в том, что нет надежного способа узнать, что находится в теле, без вызова res.json() или res.text() , и всегда вызывать оба на всякий случай слишком избыточный. В противном случае можно было бы посмотреть на заголовок Content-Length , но я не уверен, всегда ли он будет надежным. Другой вариант - анализировать JSON только в том случае, если Content-Type равно application/json , но также возможно, что некоторые API могут установить этот заголовок общим для всех ответов и даже для пустого ответа 204.

Поскольку ссылка rest создает networkError для любого кода состояния выше 300, синтаксический анализ ответа должен быть достаточно специализированным, чтобы правильно обрабатывать коды состояния 2xx. Из тех, с которыми можно было бы разумно столкнуться, это:

  • 200 ОК
  • 201 Создано
  • 202 Принято
  • 204 Нет содержимого
  • (любой из других?)

только 204 ответа должны иметь пустое тело. Будет ли возможность проверить код состояния и вернуть значение по умолчанию {} когда оно равно 204? Хотя это был бы тест для очень специфического пограничного случая, он сделал бы библиотеку более совместимой со стандартом HTTP, что, кажется, хорошо.

@fbartho. Вы бы предпочли один из этих подходов? Я сделаю PR, если один из этих вариантов или их комбинация звучит разумно.

Может быть, здесь есть средний путь? Можно ли поддерживать оба поведения?

Я очень поддерживаю интерпретацию 204 как {} по умолчанию сам, или поскольку «null» является нулевым семантическим эквивалентом отсутствия содержимого в JSON?

Я думаю, чтобы вышестоящая интерпретация пустого тела GraphQL работала, мы должны вернуть {} что затем приведет к

data: {
  __typename: "...",
  NoResponse: null,
}

Когда вы говорите: «Можно ли поддержать оба поведения?» Вы имеете в виду, глядя на код состояния 204 и глядя на заголовки? Я предполагаю, что когда на самом деле присутствует Content-Length , а также 0 тогда мы могли бы принять это как еще одно указание на пустое тело и вернуть значение по умолчанию.

Хотя я ранее предлагал заголовок Content-Type самом деле я не совсем понимаю, что делать, если для него установлено что-то иное, чем json . Я думаю, что для интерпретации этого заголовка, вероятно, потребуется использовать параметр конфигурации responseSerializer аналогичный предложенному bodySerializer . Это, вероятно, излишество и не решило бы, в частности, проблему пустого тела.

Я счастлив сделать реализацию 204 и Content-Length.

До сих пор я следовал предыдущему совету и реализовал настраиваемую выборку для обработки ответа 204. Но мне было трудно получить должный ответ. Было бы здорово иметь возможность получить эту функциональность из коробки.

К сожалению, я все еще учусь ориентироваться в мире JS ...

Исправлено через # 111!

Сладкий! Большое спасибо.

Теперь осталась только одна небольшая проблема :) Сразу после того, как я разработал собственное временное решение, я столкнулся с еще одной проблемой с REST API моего целевого сервера SharePoint ... Если вы удалите запись, вы получите ответ 200 без содержания тела :)

Можем ли мы потенциально обобщить поведение, например, если нет основного содержимого, ответьте тегом «NoResponse»? Если я не ошибаюсь, в настоящее время патч касается только 204 особого случая.

@isopterix Если сервер Content-Length в ноль, он должен работать даже с 200 ответами, намерение состоит в том, чтобы проверить и это.

С нетерпением жду возможности испытать его.

Охватывает ли это какой-либо успешный ответ с пустым содержимым? Как 200 без содержания?

Да. Исправление, которое было объединено для этого, проверяет статус 204 или заголовок Content-Length: 0 и возвращает пустой объект.

Если у вас есть пустой ответ 200 без заголовка Content-Length он не будет работать, поскольку тело не анализируется для этой проверки.

@isopterix вы можете подтвердить, что это работает? Я пытаюсь произвести мутацию, которая дает мне статус 200 и Content-Length: 0 . Однако я все еще получаю Network error: Unexpected end of JSON input . Пользуюсь версией 0.7.0.

Я вижу, что заголовки ответов пусты при отладке apollo-link-rest , в то время как мой браузер фактически показывает заголовки. apollo-link-rest манипулирует / сбрасывает заголовки в объекте ответа?

Apollo-link-rest не управляет заголовками ответов @dljcollette! надеюсь, это поможет

Я встречаю ту же ошибку

есть ли обходной путь для статуса 200 с пустым телом?

@thomaszdxsn единственный способ учесть это - использовать res.text() вместо res.json() и проверить действительный JSON перед синтаксическим анализом с помощью JSON.parse() . Это означает, что вы теряете потоковый синтаксический анализ JSON, и поэтому вряд ли что-то будет сделано.

Лучшим решением было бы изменить ваш API, чтобы он использовал семантически правильный код состояния 204, когда тело пусто, или чтобы вернуть что-то в теле ответа 200. Я думаю, что даже пустой объект в кодировке JSON будет работать, если в теле ответа есть что-то действительное JSON.

Если вы действительно не можете изменить API, возможно, вы можете передать пользовательскую функцию fetch в apollo-link-rest вы могли бы проверить и изменить ответ, прежде чем в конечном итоге вернуться для обработки ALR. Это будет взлом, но он может сработать для вас.

Или, опять же, просто убедитесь, что API возвращает заголовок Content-length: 0 в ответе.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги