Apollo-link-rest: Erreur lorsque la réponse REST est vide

Créé le 7 mai 2018  ·  18Commentaires  ·  Source: apollographql/apollo-link-rest


J'ai une demande de mutation qui, en cas de succès, répond avec un statut 204 "Aucun contenu". Lorsqu'il est associé à apollo-link-error, j'obtiens constamment une erreur réseau: fin inattendue de l'entrée JSON .

Il semble qu'apollo-link-rest essaie d'analyser le corps vide de la réponse.

Des idées pour atténuer cela? BTW J'essaie de parler à une API REST SharePoint, donc je ne peux pas modifier la réponse du serveur.

Il devrait y avoir un moyen de dire à apollo-link-rest comment gérer une réponse vide ...

Mon appel de mutation:

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

La requête gql correspondante:

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" est évidemment _null_ car il n'y a pas de réponse, mais là encore je ne peux pas envoyer une mutation sans aucun champ de réponse ... sauf si je manque quelque chose.

enhancement💡 feature help wanted 🛠 question❔

Commentaire le plus utile

Je pense que pour que l'interprétation en amont GraphQL du corps vide fonctionne, nous devrions retourner {} ce qui entraînera alors

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

Lorsque vous dites "Les deux comportements pourraient-ils être pris en charge?" voulez-vous dire regarder le code d'état 204 et regarder les en-têtes? Je suppose que quand le Content-Length est réellement présent et aussi 0 alors nous pourrions prendre cela comme une autre indication d'un corps vide et retourner la valeur par défaut.

Bien que j'aie proposé l'en-tête Content-Type plus tôt, je ne suis pas vraiment clair sur ce qu'il faut faire quand il est défini sur autre chose que json . Je pense que si cet en-tête doit être interprété, il devra probablement être fait avec une option de configuration responseSerializer similaire à la proposition bodySerializer . C'est probablement exagéré cependant, et ne résoudrait pas le problème du corps vide en particulier.

Je suis heureux de faire l'implémentation 204 et Content-Length.

Tous les 18 commentaires

@isopterix - Je ne pense pas que nous ayons vraiment un moyen facile de gérer cela pour le moment. - Ma première pensée serait de vous recommander d'encapsuler Fetch et de fournir une implémentation Fetch personnalisée qui remplace le corps de 204 réponses par {} - Je pense que sans créer une meilleure fonctionnalité, c'est la seule façon de le faire aujourd'hui!

@isopterix J'ai essayé de reproduire ce problème avec le test ci-dessous, mais le test réussit pour un corps vide. Il semble que fetch response.json() renvoie {} lorsque le corps est une chaîne vide. Pourriez-vous essayer de retracer cela plus étroitement dans votre configuration en direct pour voir où l'erreur se produit?

  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, peu importe mon dernier commentaire. Il semble que les implémentations du navigateur soient moins indulgentes que l'environnement de test.

Le seul problème que je vois avec la détection d'un corps vide est qu'il n'y a pas de moyen sûr de dire ce qu'il y a dans le corps sans appeler res.json() ou res.text() , et toujours appeler les deux au cas où c'est trop redondant. Sinon, on pourrait regarder l'en-tête Content-Length , mais je ne suis pas trop sûr s'il sera toujours fiable. Une autre option serait d'analyser JSON uniquement si le Content-Type est application/json , mais il est également concevable que certaines API puissent définir cet en-tête en commun pour toutes les réponses et même sur une réponse 204 vide.

Puisque le lien de repos produit un networkError sur tout code d'état supérieur à 300, l'analyse de la réponse doit seulement être suffisamment spécialisée pour gérer correctement les codes d'état 2xx. Parmi ceux que l'on peut raisonnablement s'attendre à rencontrer, c'est:

  • 200 OK
  • 201 Créé
  • 202 Accepté
  • 204 Pas de contenu
  • (l'un des autres?)

les 204 réponses devraient être les seules à avoir un corps vide. Serait-ce une option pour inspecter le code d'état et retourner un {} par défaut quand il est 204? Bien que ce soit un test pour un cas de bord très spécifique, cela rendrait la bibliothèque plus conforme à la norme HTTP, ce qui semble être une bonne chose.

@fbartho auriez-vous une préférence pour l'une de ces approches? Je ferai le PR si l'une de ces options, ou une combinaison de celles-ci, semble raisonnable.

Peut-être y a-t-il une voie médiane ici? Les deux comportements pourraient-ils être pris en charge?

Je suis assez favorable à l'interprétation de 204 comme {} par défaut moi-même, ou comme "null" est nul l'équivalent sémantique de l'absence de contenu dans JSON?

Je pense que pour que l'interprétation en amont GraphQL du corps vide fonctionne, nous devrions retourner {} ce qui entraînera alors

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

Lorsque vous dites "Les deux comportements pourraient-ils être pris en charge?" voulez-vous dire regarder le code d'état 204 et regarder les en-têtes? Je suppose que quand le Content-Length est réellement présent et aussi 0 alors nous pourrions prendre cela comme une autre indication d'un corps vide et retourner la valeur par défaut.

Bien que j'aie proposé l'en-tête Content-Type plus tôt, je ne suis pas vraiment clair sur ce qu'il faut faire quand il est défini sur autre chose que json . Je pense que si cet en-tête doit être interprété, il devra probablement être fait avec une option de configuration responseSerializer similaire à la proposition bodySerializer . C'est probablement exagéré cependant, et ne résoudrait pas le problème du corps vide en particulier.

Je suis heureux de faire l'implémentation 204 et Content-Length.

Jusqu'à présent, j'ai suivi les conseils précédents et mis en œuvre une extraction personnalisée pour gérer la réponse 204. Mais j'ai eu du mal à obtenir une réponse appropriée. Avoir la possibilité de sortir cette fonctionnalité de la boîte serait génial.

Malheureusement, j'apprends encore à naviguer dans le monde JS ...

Corrigé via # 111!

Doux! Merci beaucoup.

Maintenant, il ne reste plus qu'un petit problème :) Juste après avoir élaboré ma propre solution temporaire, j'ai rencontré un autre problème avec l'API REST de mon SharePoint Server cible ... Si vous supprimez une entrée, vous obtenez une réponse 200 sans contenu corporel :)

Pourrait-on potentiellement généraliser le comportement, c'est-à-dire s'il n'y a pas de corps de contenu répondre avec la balise "NoResponse"? Si je ne me trompe pas, le patch ne traite actuellement que du cas spécial 204.

@isopterix Si le serveur met correctement le Content-Length à zéro, il devrait fonctionner même sur 200 réponses, l'intention est de vérifier cela également.

Au plaisir de lui donner un tour.

Cela couvre-t-il une réponse réussie avec un contenu vide? Comme un 200 sans contenu?

Oui. Le correctif qui a été fusionné pour cela vérifie un statut 204 ou un en-tête Content-Length: 0 et renvoie un objet vide.

Si vous avez une réponse 200 vide sans un en-tête Content-Length cela ne fonctionnera pas car le corps n'est pas analysé pour cette vérification.

@isopterix pouvez-vous confirmer que cela fonctionne? J'essaye de faire une mutation qui me donne un statut 200 et Content-Length: 0 . Cependant, je reçois toujours un Network error: Unexpected end of JSON input . J'utilise la version 0.7.0.

Je vois que les en-têtes de réponse sont vides lors du débogage de apollo-link-rest , alors que mon navigateur affiche en fait les en-têtes. Est-ce que apollo-link-rest manipule / réinitialise les en-têtes dans l'objet de réponse?

Apollo-link-rest ne manipule pas les en-têtes de réponse @dljcollette! J'espère que cela pourra aider

Je rencontre la même erreur

a une solution de contournement pour le statut 200 avec un corps vide?

@thomaszdxsn, la seule façon d'accommoder cela serait d'utiliser res.text() au lieu de res.json() et de vérifier si JSON valide avant d'analyser avec JSON.parse() . Cela signifie que vous perdez sur l'analyse JSON en continu et que, en tant que tel, il est peu probable que quelque chose soit fait.

La meilleure solution serait de modifier votre API afin qu'elle utilise le code d'état sémantiquement correct 204 lorsque le corps est vide, ou de renvoyer quelque chose dans le corps de la réponse 200. Je pense que même un objet encodé JSON vide fonctionnerait, tant qu'il y a juste quelque chose dans le corps de la réponse qui est JSON valide.

Si vous ne pouvez vraiment pas changer l'API, vous pouvez peut-être passer une fonction personnalisée fetch dans apollo-link-rest vous pourriez inspecter et modifier la réponse avant de retourner finalement pour ALR à traiter. Ce sera un hack, mais cela pourrait fonctionner pour vous.

Ou, encore une fois, assurez-vous simplement que l'API renvoie l'en-tête Content-length: 0 dans la réponse.

Cette page vous a été utile?
0 / 5 - 0 notes