Apollo-link-rest: Error cuando la respuesta REST está vacía

Creado en 7 may. 2018  ·  18Comentarios  ·  Fuente: apollographql/apollo-link-rest


Tengo una solicitud de mutación que, cuando tiene éxito, responde con un estado 204 "Sin contenido". Cuando se combina con apollo-link-error, constantemente recibo un error de red: final inesperado de la entrada JSON .

Parece que apollo-link-rest está tratando de analizar el cuerpo vacío de la respuesta.

¿Alguna idea de cómo mitigar esto? Por cierto, estoy tratando de hablar con una API REST de SharePoint, por lo que no hay mucho que pueda modificar la respuesta del servidor.

Debería haber una forma de decirle a apollo-link-rest cómo lidiar con una respuesta vacía ...

Mi llamada de mutación:

                  <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 consulta de gql correspondiente:

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" es obviamente _null_ ya que no hay respuesta, pero de nuevo no puedo enviar una mutación sin ningún campo de respuesta ... a menos que me falte algo.

enhancement💡 feature help wanted 🛠 question❔

Comentario más útil

Creo que para que funcione la interpretación de GraphQL ascendente del cuerpo vacío, deberíamos devolver {} que luego resultará en

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

Cuando dice "¿Se pueden apoyar ambos comportamientos?" ¿te refieres a mirar el código de estado 204 y mirar los encabezados? Supongo que cuando Content-Length está realmente presente y también 0 , podríamos tomar eso como otra indicación de un cuerpo vacío y devolver el valor predeterminado.

Aunque propuse el encabezado Content-Type anteriormente, en realidad no tengo muy claro qué se debe hacer cuando se establece en algo diferente a json . Creo que si se va a interpretar este encabezado, probablemente deba hacerse con una opción de configuración responseSerializer similar a la propuesta bodySerializer . Sin embargo, eso probablemente sea excesivo y no abordaría el problema del cuerpo vacío en particular.

Estoy feliz de hacer la implementación 204 y Content-Length.

Todos 18 comentarios

@isopterix : no creo que tengamos una manera fácil de lidiar con esto en este momento. - Mi pensamiento inicial sería recomendarle que envuelva Fetch y proporcione una implementación personalizada de Fetch que reemplace el cuerpo de 204 respuestas con {} - Creo que sin crear una función mejor, esa es la única forma en que podría hacer esto ¡hoy!

@isopterix Traté de reproducir este problema con la prueba a continuación, pero la prueba pasa para un cuerpo vacío. Parece que buscar response.json() devuelve {} cuando el cuerpo es una cadena vacía. ¿Podría intentar rastrear esto más de cerca en su configuración en vivo para ver dónde ocurre el error?

  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 no importa mi último comentario. Parece que las implementaciones del navegador son menos indulgentes que el entorno de prueba.

El único problema que veo al detectar un cuerpo vacío es que no hay una forma segura de saber qué hay en el cuerpo sin llamar a res.json() o res.text() , y siempre llamar a ambos por si acaso es demasiado redundante. De lo contrario, uno podría mirar el encabezado Content-Length , pero no estoy muy seguro de si siempre será confiable. Otra opción sería solo analizar JSON si Content-Type es application/json , pero también es posible que algunas API establezcan ese encabezado en común para todas las respuestas e incluso en una respuesta 204 vacía.

Dado que el enlace restante produce un networkError en cualquier código de estado por encima de 300, el análisis de respuesta solo necesita ser lo suficientemente especializado para manejar correctamente los códigos de estado 2xx. De los que uno podría esperar encontrar razonablemente, son:

  • 200 OK
  • 201 Creado
  • 202 Aceptado
  • 204 Sin contenido
  • (¿alguno de los otros?)

las 204 respuestas deben ser las únicas que tengan un cuerpo vacío. ¿Sería una opción inspeccionar el código de estado y devolver un {} predeterminado cuando es 204? Si bien sería una prueba para un caso de borde muy específico, haría que la biblioteca fuera más compatible con el estándar HTTP, lo que parece ser algo bueno.

@fbartho, ¿tendrías alguna preferencia por uno de estos enfoques? Haré el PR si una de estas opciones, o una combinación de las mismas, suena razonable.

¿Quizás hay un camino intermedio aquí? ¿Podrían apoyarse ambos comportamientos?

Soy bastante partidario de interpretar 204 como {} por defecto yo mismo, o como "nulo" es nulo el equivalente semántico de ningún contenido en JSON.

Creo que para que funcione la interpretación de GraphQL ascendente del cuerpo vacío, deberíamos devolver {} que luego resultará en

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

Cuando dice "¿Se pueden apoyar ambos comportamientos?" ¿te refieres a mirar el código de estado 204 y mirar los encabezados? Supongo que cuando Content-Length está realmente presente y también 0 , podríamos tomar eso como otra indicación de un cuerpo vacío y devolver el valor predeterminado.

Aunque propuse el encabezado Content-Type anteriormente, en realidad no tengo muy claro qué se debe hacer cuando se establece en algo diferente a json . Creo que si se va a interpretar este encabezado, probablemente deba hacerse con una opción de configuración responseSerializer similar a la propuesta bodySerializer . Sin embargo, eso probablemente sea excesivo y no abordaría el problema del cuerpo vacío en particular.

Estoy feliz de hacer la implementación 204 y Content-Length.

Hasta ahora seguí el consejo anterior e implementé una búsqueda personalizada para manejar la respuesta 204. Pero tuve dificultades para obtener una respuesta adecuada. Tener la opción de sacar esta funcionalidad de la caja sería increíble.

Desafortunadamente, todavía estoy aprendiendo a navegar por el mundo JS ...

¡Arreglado a través del # 111!

¡Dulce! Muchas gracias.

Ahora, solo queda un pequeño problema :) Justo después de que resolví mi propia solución temporal, encontré otro problema con la API REST de mi servidor SharePoint de destino ... Si elimina una entrada, obtiene una respuesta 200 sin contenido corporal :)

¿Podríamos potencialmente generalizar el comportamiento, es decir, si no hay contenido del cuerpo, responda con la etiqueta "NoResponse"? Si no me equivoco, el parche actualmente solo aborda el caso especial 204.

@isopterix Si el servidor establece Content-Length en cero correctamente, debería estar funcionando incluso en 200 respuestas, la intención es verificar eso también.

Deseando darle una vuelta.

¿Esto cubre alguna respuesta exitosa con contenido vacío? ¿Como un 200 sin contenido?

Si. La corrección que se combinó para esto comprueba un estado 204 o un encabezado Content-Length: 0 y devuelve un objeto vacío.

Si tiene una respuesta 200 vacía sin un encabezado Content-Length , no funcionará ya que el cuerpo no se analiza para esta verificación.

@isopterix, ¿puedes confirmar que esto funciona? Estoy intentando hacer una mutación que me da un estado 200 y Content-Length: 0 . Sin embargo, sigo obteniendo un Network error: Unexpected end of JSON input . Estoy usando la versión 0.7.0.

Veo que los encabezados de respuesta están vacíos al depurar apollo-link-rest , mientras que mi navegador muestra los encabezados. ¿ apollo-link-rest manipula / restablece los encabezados en el objeto de respuesta?

¡Apollo-link-rest no manipula los encabezados de respuesta @dljcollette! Espero que ayude

Me encuentro con el mismo error

¿Tiene alguna solución para el estado 200 con cuerpo vacío?

@thomaszdxsn, la única forma de adaptarse a eso sería usar res.text() lugar de res.json() y verificar si hay JSON válido antes de analizar con JSON.parse() . Esto significa que pierde el análisis de JSON de transmisión y, como tal, no es algo que se pueda hacer.

La mejor solución sería cambiar su API para que use el código de estado 204 semánticamente correcto cuando el cuerpo está vacío, o devolver algo en el cuerpo de respuesta 200. Creo que incluso un objeto codificado en JSON vacío funcionaría, siempre que haya algo en el cuerpo de respuesta que sea JSON válido.

Si realmente no puede cambiar la API, tal vez pueda pasar una función fetch en apollo-link-rest donde podría inspeccionar y cambiar la respuesta antes de regresar para que ALR la procese. Será un truco, pero podría funcionar para ti.

O, nuevamente, simplemente asegúrese de que la API devuelva el encabezado Content-length: 0 en la respuesta.

¿Fue útil esta página
0 / 5 - 0 calificaciones