RESTful API representa la ausencia de una cosa

18

Imagine una API para identificar si una persona ha seleccionado su animal espiritual. Solo pueden tener cero o un espíritu animal.

Actualmente:

/person/{id}/selectedSpiritAnimal

cuando han seleccionado un animal devuelve http 200 y {selectedAnimal:mole}

pero cuando no tienen selección, devuelve http 404.

Esto hace que mi espíritu animal sea infeliz ya que estamos representando un problema de dominio válido, ya que aún no hemos seleccionado un espíritu animal, como un error HTTP.

Además, como empresa, erm Sprit-Animal-Hampers-R-us, queremos saber cuándo alguien no tiene una selección para poder avisarle.

¿Cuál es una mejor respuesta aquí:

HTTP 200 y {selectedAnimal:null}

o incluso más explícito

HTTP 200 y {selectedAnimal:null, spiritAnimalSelected: false}

¿O es mejor devolver un 404? Dado que al igual que this image has not yet been uploadedal ver una imagen en línea, sería un 404. this person has not selected a spirit animalpodría ser un 404


Esta pregunta se ha propuesto como un duplicado, pero esa pregunta aborda una URL válida que se solicita cuando la aplicación se ha configurado para no permitir el cambio que representa la URL.

Mientras que aquí estoy viendo cómo uno representa un recurso donde la ausencia del recurso es significativa. Es decir, es válido que el cliente solicite la URL y la respuesta es que ha solicitado con éxito el recurso que representa la ausencia de una cosa.

Entonces, esto no es 'lógica de negocios', sino más bien una circunstancia en la que la ausencia de una cosa tiene sentido (puede ser que muchos de mis colegas estén argumentando que 404 todavía es correcto) pero no estoy seguro de cómo asignar eso al Especificaciones.


Muy difícil elegir una respuesta. He cambiado de opinión varias veces sobre la conversación aquí y la que está en curso en el trabajo.

Lo que me soluciona aquí es que la especificación dice que un 4xx es cuando el cliente ha cometido un error . En este caso, se le ha dicho al cliente que espere una respuesta de la url seleccionada de SpiritAnimal, por lo que no se ha equivocado.

El consenso entre mis colegas es que este es un síntoma de un mal diseño de API

Probablemente sería mejor que simplemente solicitemos / person / {id} y eso devuelva un conjunto de relaciones de enlace para la persona ... entonces, si no recibe el enlace / selectedSpiritAnimal (cuando una persona no tiene selección) pero usted llamarlo de todos modos, entonces un 404 tiene sentido. O que implemente respuestas parciales y deje que / person / {id} devuelva un documento más completo a menos que el cliente solicite un subconjunto de datos

Paul D'Ambra
fuente
55
No ha explicado por qué cree que es un problema devolver un 404. Desde el punto de vista del dominio, no sabe si recibió un 404 o un 200, que es abstraído por la capa del cliente.
Vincent Savard
14
o tal vez es mejor devolver 204 sin contenido?
Paul D'Ambra
66
Supongo que mi preocupación con 404 es la desambiguación de un cambio de configuración que introduce una plantilla de URL incorrecta de una persona sin espíritu animal
Paul D'Ambra
2
@ PaulD'Ambra Para lo que vale, no usaría un sin contenido. No he visto los códigos HTTP manejados en el front-end de esa manera en Javascript desde los días XmlHttpRequest. Pero sin duda es válido.
Brandon Arnold

Respuestas:

24

Los códigos HTTP 4xx probablemente no sean la opción correcta para este escenario. Usted declara que tener cero animales espirituales es un estado válido, y la ruta API person/{id}/selectedSpiritAnimaltendrá en cuenta si la persona idtiene o no uno.

Las respuestas HTTP 4xx están reservadas para la situación en la que un cliente ha hecho algo incorrecto en la solicitud (consulte el archivo de w3 de la especificación original ). Pero el cliente está haciendo una solicitud válida, ya sea que la persona idtenga o no un animal espiritual.

Entonces me inclino hacia las segundas soluciones usando un cuerpo JSON formateado correctamente en la respuesta y un código HTTP 2xx.

Ahora, si recibe una solicitud de este tipo y resulta que la persona idno existe, un código 4xx tiene más sentido.

Brandon Arnold
fuente
17
"Las respuestas HTTP 4xx están reservadas para la situación en la que un cliente ha hecho algo incorrecto en la solicitud". Al hacer declaraciones autorizadas sobre la especificación, consulte la especificación HTTP real y no (a) un marco construido sobre ella, o (b) una publicación de blog aleatoria.
Eric Stein
55
@EricStein Me sentí un poco flojo al respecto; Gracias por la crítica. Actualizado.
Brandon Arnold
1
Siento que el enlace RFC aquí es un poco conflictivo. Por un lado, la parte 4xx dice cases in which the client seems to have erred, pero por el otro, el 404 dice directamente The server has not found anything matching the Request-URI. Podría considerar solicitar algo que no exista un error del cliente. Entiendo que la persona no existe frente a un subproceso no existente, pero eso podría indicarse como el mensaje de respuesta. 204 también parece relevante, ya que la entidad existe, pero no tiene contenido para una subpropiedad.
shortstuffsushi
1
El cliente hizo algo mal; solicitó el animal espiritual seleccionado para un usuario cuando no existe. Eso es exactamente lo que significa 404 ... has pedido algo que no está allí.
Andy
55
Cuando mi navegador realiza una solicitud a softwareengineering.stackexchange.com/questions/unicorn , es una solicitud válida y, sin embargo, sigo recibiendo un 404 (simplemente no hay duda con la identificación "unicornio").
user253751
24

Déjame presentarte el modelo de madurez de Richardson .

Su problema es que está representando dos recursos como uno, donde realmente debería tener dos recursos que tengan una relación indicada por hipermedia. Usar hipermedia para describir relaciones es el glorioso nivel 3 de Descanso.

Su persona debe vivir debajo del URI /person/{id}y el animal debe vivir debajo /spiritanimal/{id}. La persona debe indicar que tiene un espíritu animal usando un enlace al animal.

Imaginemos una persona llamada Bob, que tiene id 123 y un animal espíritu Unicornio.

GET /person/123

volvería;

{
  "name": "Bob",
  "links": [
    {
      "rel": "spiritanimal",
      "uri": "/spiritanimals/789"
    }
  ]
}

Ahora, cualquiera que lea a la persona 123 sabrá que tiene un animal espiritual y que tiene el URI donde puede obtener más información sobre él.

GET /spitiranimal/789

podría regresar

{
  "type": "Unicorn"
}

Ahora imaginemos a una persona llamada Fred, que tiene id 456 y no tiene espíritu animal.

GET /person/456

volvería;

{
  "name": "Fred",
  "links": [
  ]
}

Ahora, cualquiera que lea a la persona 456 sabrá que no tiene un espíritu animal, ya que no hay ningún vínculo. No es necesario utilizar ningún código de estado HTTP para representar la falta de una relación.

Qwerky
fuente
1
Simplemente estaba agregando a la pregunta que, dada la capacidad de cambiar la API, algún nivel de HATEOAS probablemente sería la solución correcta. Ese es un gran ejemplo de eso
Paul D'Ambra el
1

Esta es la url apropiada para obtener animales espirituales; por lo tanto, un error 404 es inapropiado. 404 es para representar un problema técnico, no un problema lógico.

La solución adecuada es devolver http 200 y {"selectedAnimal": null}

Debería tener un método web separado/person/{id}/hasSelectedSpiritAnimal que regrese {"isSpiritAnimalSelected": false}. Detrás de escena, puede o no hacer las mismas llamadas de método, simplemente devolviendo falso si es nulo, pero eso depende de él, no del código consumidor.

Es mejor evitar combinar para separar consultas en un método web sin una razón convincente para hacerlo, incluso si las consultas están estrechamente relacionadas.

TheCatWhisperer
fuente
1
¿Podría explicar por qué cree que esta es la solución adecuada? No tengo sentido devolver datos cuando no hay datos para devolver. Estoy de acuerdo con usted en que un 404 no tiene sentido ya que la URL está bien, por lo que un 204 parece tener más sentido para mí.
Vincent Savard
2
Los códigos de estado de @VincentSavard son más difíciles de trabajar que las respuestas json, y son más apropiados para problemas técnicos en lugar de comunicar información de dominio.
TheCatWhisperer
2
204: Sin contenido con un cuerpo vacío. Todo es claro y se explica por sí mismo. No hay necesidad de discutir más. Con todo, cuando no hay un patrón de diseño claro, un SE debe elegir uno, doblarlo según sus necesidades y proporcionar documentación. Devolver un cuerpo con una respuesta 204 no es consecuente.
formixian
2
@formixian ... Si estuviera creando una api bjson / tcp en lugar de una json / http, ¿qué devolvería para este caso? Confiar en los códigos http me parece que vincula innecesariamente los resultados de la API a su implementación http.
TheCatWhisperer
2
@VincentSavard When you said "the spirit animal is not currently on file", it seemed quite similar to me to "there is no content"Sin contenido significa que no hay contenido . es decir: devuelve "". Estos dos no son similares en absoluto. "El espíritu animal no está actualmente en el archivo" es muy diferente de "".
Shane
1

Lo que representa su punto final no es solo un animal; Es un animal o falta de él. Es un valor mejor representado por un Optional/ Maybe/ Nullable/ etc.

Por lo tanto, los valores legítimos (como en 200 OK) pueden ser:

  • {'animal': <some animal>, 'selected': true}
  • {'animal': null, 'selected': false}

Me imagino que ese DELETEmétodo, cuando se aplica al punto final, puede establecerse 'selected'de falsenuevo, es decir, desarmar el animal seleccionado.

Puede, por supuesto, soltar la 'selected'clave aquí, solo se muestra para mayor claridad; string vs nulles suficiente para la distinción.

9000
fuente
0

Deberías usar 404.

El código de estado 404 (No encontrado) indica que el servidor de origen no encontró una representación actual para el recurso de destino

RFC7231

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 404 Not Found
Content-Type: text/plain
Date: Mon, 25 Sep 2017 00:00:00 GMT

this person has not selected a spirit animal

Como está creando una interfaz de programación, no una interfaz humana, el texto 404 es opcional.

Prefiero esto porque prefiero los protocolos estándar tanto como sea posible. HTTP tiene una forma de representar la no existencia, y eso es lo que usaría.

EDITAR: suponga que agregó una función en la que los usuarios pueden elegir si desean compartir sus espíritus animales y alguien no los comparte. ¿Devolverías 200 OK nullo 200 OK "Unauthorized? ¿O utilizaría el estándar 401 no autorizado / 403 prohibido? Esto parece directamente análogo a no elegir uno en primer lugar.


Alternativamente, si desea utilizar 200 OK + JSON, debe regresar null.

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

null

Mantén las cosas limpias. Cree más envoltura solo si es necesario.

Paul Draper
fuente
2
Sin embargo, los datos se encuentran. Simplemente sucede que los datos son null(no tienen valor). Esto es diferente al cliente que solicita algo para lo cual no existe un espacio para mantener el valor. No sugeriría lanzar un AttributeErroren Python cuando el valor sea None; eso haría que la interfaz fuera poco práctica e innecesariamente difícil de trabajar. Más pragmáticamente, muchas API de clientes HTTP podrían convertir el 404 en el mecanismo de manejo de errores estándar del lenguaje (código de error, excepción, tipo de error), lo que significa que tendría que suprimir un error extraño.
jpmc26
@Paul Draper Desplácese un poco hacia arriba en la especificación, verá que hace una declaración general sobre HTTP 4xx: "La clase de código de estado 4xx está destinada a casos en los que el cliente parece haber errado". La operación ha declarado claramente que no tener un espíritu animal es un estado válido para el cual la API debe tener en cuenta. tools.ietf.org/html/rfc7231#section-6.5
Brandon Arnold
Entonces, ¿cómo distingue entre indicar la inexistencia del recurso solicitado (es decir, no se seleccionó ningún animal) y el cliente solicita una ruta no válida (es decir /person/{id}/selectedSpritAnimal, observa el error tipográfico Sprit).
sampathsris
1
Independientemente de la intención, un 404 significa estrictamente que no se encontró el recurso. Si un SelectedSpiritAnimal es un recurso y no existe ninguno, entonces no hay nada que OBTENER y un 404 es apropiado. Sin embargo, si el cliente quiere saber si se seleccionó un animal espiritual, entonces el servidor debe exponer otro recurso /person/{id}/isSpiritAnimalSelectedque le dirá al cliente si se seleccionó uno. Me parece el mismo ejemplo clásico de probar si existe un archivo antes de leerlo. Simplemente lea el archivo y maneje el error ENOENT si se lanza.
aaaaaa
1
@PaulDraper El punto no es si el recurso existe o no. El punto es que los códigos HTTP 4xx están destinados a cuando la persona que llama está utilizando API incorrectamente. Op afirma que /person/{id}/selectedSpiritAnimalestá destinado a ser utilizado incluso si el animal espiritual no existe. Esto significa que HTTP 4xx es incorrecto, cuando se usa en tal caso. Op también afirma correctamente que esto puede ser un olor a mal diseño de API.
Brandon Arnold