Apollo-link-rest: Erro quando a resposta REST está vazia

Criado em 7 mai. 2018  ·  18Comentários  ·  Fonte: apollographql/apollo-link-rest


Tenho uma solicitação de mutação que, quando bem-sucedida, responde com um status 204 "Sem conteúdo". Quando combinado com apollo-link-error, estou constantemente recebendo um erro de rede: Fim inesperado da entrada JSON .

Parece que apollo-link-rest está tentando analisar o corpo vazio da resposta.

Alguma ideia de como mitigar isso? BTW, estou tentando falar com uma API REST do SharePoint, então não há muito que eu possa ajustar a resposta do servidor.

Deve haver uma maneira de dizer ao apollo-link-rest como lidar com uma resposta vazia ...

Minha chamada de mutação:

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

A consulta gql correspondente:

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" é obviamente _nulo_, pois não há resposta, mas, novamente, não posso enviar uma mutação sem nenhum campo de resposta ... a menos que esteja faltando alguma coisa.

enhancement💡 feature help wanted 🛠 question❔

Comentários muito úteis

Acho que para que a interpretação do GraphQL upstream do corpo vazio funcione, devemos retornar {} que resultará em

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

Quando você diz "Ambos os comportamentos podem ser suportados?" você quer dizer olhar para o código de status 204 e olhar para os cabeçalhos? Eu acho que quando Content-Length está realmente presente e também 0 então podemos tomar isso como outra indicação de um corpo vazio e retornar o padrão.

Embora eu tenha proposto o cabeçalho Content-Type anteriormente, não estou exatamente certo sobre o que deve ser feito quando ele está definido como algo diferente de json . Eu acho que se este cabeçalho for interpretado, provavelmente precisará ser feito com uma opção responseSerializer config semelhante à bodySerializer proposta. Isso provavelmente é um exagero e não resolveria o problema do corpo vazio em particular.

Estou feliz em fazer a implementação 204 e Content-Length.

Todos 18 comentários

@isopterix - Não acho que tenhamos uma maneira fácil de lidar com isso agora. - Meu pensamento inicial seria recomendar que você envolva o Fetch e forneça uma implementação personalizada do Fetch que substitua o corpo de 204 respostas por {} - Acho que sem construir um recurso melhor, essa é a única maneira de fazer isso hoje!

@isopterix Tentei reproduzir este problema com o teste abaixo, mas o teste passa para um corpo vazio. Parece que fetch response.json() retorna {} quando o corpo é uma string vazia. Você poderia tentar rastrear isso mais de perto em sua configuração ao vivo para ver onde o erro ocorre?

  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 deixa

O único problema que vejo ao detectar um corpo vazio é que não há uma maneira segura de saber o que está no corpo sem chamar res.json() ou res.text() , e sempre chamar ambos para o caso de ser demais redundante. Caso contrário, pode-se olhar para o cabeçalho Content-Length , mas não tenho certeza se ele sempre será confiável. Outra opção seria apenas analisar JSON se Content-Type for application/json , mas também é concebível que algumas APIs possam definir esse cabeçalho em comum para todas as respostas e até mesmo em uma resposta 204 vazia.

Uma vez que o link restante produz um networkError em qualquer código de status acima de 300, a análise da resposta só precisa ser suficientemente especializada para lidar com os códigos de status 2xx corretamente. Daqueles que se pode razoavelmente esperar encontrar, sendo:

  • 200 OK
  • 201 criado
  • 202 aceito
  • 204 Sem Conteúdo
  • (algum dos outros?)

as 204 respostas devem ser as únicas a ter um corpo vazio. Seria uma opção inspecionar o código de status e retornar um {} padrão quando for 204? Embora fosse um teste para um caso extremo muito específico, tornaria a biblioteca mais compatível com o padrão HTTP, o que parece ser uma coisa boa.

@fbartho , você teria alguma preferência por uma dessas abordagens? Farei o PR se uma dessas opções, ou uma combinação delas, soar razoável.

Talvez haja um caminho do meio aqui? Ambos os comportamentos podem ser suportados?

Apoio bastante a interpretação de 204 como {} por padrão, ou como “null” é nulo o equivalente semântico de nenhum conteúdo em JSON?

Acho que para que a interpretação do GraphQL upstream do corpo vazio funcione, devemos retornar {} que resultará em

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

Quando você diz "Ambos os comportamentos podem ser suportados?" você quer dizer olhar para o código de status 204 e olhar para os cabeçalhos? Eu acho que quando Content-Length está realmente presente e também 0 então podemos tomar isso como outra indicação de um corpo vazio e retornar o padrão.

Embora eu tenha proposto o cabeçalho Content-Type anteriormente, não estou exatamente certo sobre o que deve ser feito quando ele está definido como algo diferente de json . Eu acho que se este cabeçalho for interpretado, provavelmente precisará ser feito com uma opção responseSerializer config semelhante à bodySerializer proposta. Isso provavelmente é um exagero e não resolveria o problema do corpo vazio em particular.

Estou feliz em fazer a implementação 204 e Content-Length.

Até agora, segui o conselho anterior e implementei uma busca personalizada para lidar com a resposta 204. Mas tive dificuldade em obter uma resposta adequada. Ter a opção de tirar essa funcionalidade da caixa seria incrível.

Infelizmente, ainda estou aprendendo a navegar no mundo JS ...

Corrigido via # 111!

Doce! Muito obrigado.

Agora, só falta um pequeno problema :) Logo depois de descobrir minha própria solução temporária, encontrei outro problema com a API REST do meu SharePoint Server de destino ... Se você excluir uma entrada, obterá uma resposta 200 sem conteúdo corporal :)

Poderíamos potencialmente generalizar o comportamento, ou seja, se não houver conteúdo do corpo, responder com a tag "NoResponse"? Se não estou enganado, o patch atualmente aborda apenas o caso especial 204.

@isopterix Se o servidor definir Content-Length como zero corretamente, ele deve estar funcionando até mesmo com 200 respostas, a intenção é verificar isso também.

Estou ansioso para dar uma olhada.

Isso cobre alguma resposta bem-sucedida com conteúdo vazio? Gosta de 200 sem conteúdo?

sim. A correção que foi mesclada para isso verifica o status 204 ou um cabeçalho Content-Length: 0 e retorna um objeto vazio.

Se você tiver uma resposta vazia 200 sem um cabeçalho Content-Length isso não funcionará, pois o corpo não é analisado para esta verificação.

@isopterix você pode confirmar que isso funciona? Estou tentando fazer uma mutação que me dá um status 200 e Content-Length: 0 . No entanto, ainda estou recebendo Network error: Unexpected end of JSON input . Estou usando a versão 0.7.0.

Vejo que os cabeçalhos de resposta estão vazios durante a depuração de apollo-link-rest , enquanto meu navegador mostra os cabeçalhos. apollo-link-rest manipula / redefine os cabeçalhos no objeto de resposta?

O Apollo-link-rest não manipula os cabeçalhos de resposta @dljcollette! espero que ajude

Eu encontro o mesmo erro

tem alguma solução alternativa para o status 200 com corpo vazio?

@thomaszdxsn, a única maneira de acomodar isso seria usar res.text() vez de res.json() e verificar se há um JSON válido antes de analisar com JSON.parse() . Isso significa que você perde na análise JSON de streaming e, como tal, provavelmente não será feito.

A melhor solução seria alterar sua API para que ela use o código de status 204 semanticamente correto quando o corpo estiver vazio ou retornar algo no corpo da resposta 200. Acho que até mesmo um objeto codificado em JSON vazio funcionaria, desde que haja apenas algo no corpo da resposta que seja um JSON válido.

Se você realmente não pode alterar a API, talvez possa passar uma função fetch para apollo-link-rest onde você pode inspecionar e alterar a resposta antes de eventualmente retornar para o ALR processar. Vai ser um hack, mas pode funcionar para você.

Ou, novamente, apenas certifique-se de que a API retorne o cabeçalho Content-length: 0 na resposta.

Esta página foi útil?
0 / 5 - 0 avaliações