¿Qué código de estado HTTP devolver si varias acciones terminan con diferentes estados?

68

Estoy creando una API donde el usuario puede pedirle al servidor que realice varias acciones en una solicitud HTTP. El resultado se devuelve como una matriz JSON, con una entrada por acción.

Cada una de estas acciones puede fallar o tener éxito independientemente una de la otra. Por ejemplo, la primera acción podría tener éxito, la entrada a la segunda acción podría tener un formato deficiente y no podría validarse, y la tercera acción podría causar un error inesperado.

Si hubiera una solicitud por acción, devolvería los códigos de estado 200, 422 y 500 respectivamente. Pero ahora, cuando solo hay una solicitud, ¿qué código de estado debo devolver?

Algunas opciones:

  • Siempre devuelva 200 y brinde información más detallada en el cuerpo.
  • ¿Tal vez siga la regla anterior solo cuando haya más de una acción en la solicitud?
  • ¿Quizás devuelva 200 si todas las solicitudes tienen éxito, de lo contrario 500 (o algún otro código)?
  • Solo use una solicitud por acción y acepte la sobrecarga adicional.
  • ¿Algo completamente diferente?
pregunta Anders 29.08.2016 - 14:47

8 respuestas

20

La respuesta corta y directa

Dado que la solicitud habla de ejecutar la lista de tareas (las tareas son el recurso del que estamos hablando aquí), entonces si el grupo de tareas se ha adelantado a la ejecución (es decir, independientemente del resultado de la ejecución), entonces sería sensato que el estado de respuesta sea 200 OK . De lo contrario, si hubiera un problema que impidiera la ejecución del grupo de tareas, como el error de validación de los objetos de tarea , o algún servicio requerido no esté disponible, por ejemplo, el estado de la respuesta debe indicar que error. Más allá de eso, cuando comience la ejecución de las tareas, ya que las tareas a realizar se enumeran en el cuerpo de la solicitud, entonces espero que los resultados de la ejecución se enumeren en el cuerpo de la respuesta.

La respuesta larga y filosófica

Estás experimentando este dilema porque estás desviando de lo que HTTP fue diseñado. No lo está interactuando para administrar recursos, sino que lo está utilizando como medio de invocación de métodos remotos (lo cual no es muy extraño, sin embargo, funciona mal sin un esquema preconcebido).

Habiendo dicho lo anterior, y sin valor para convertir esta respuesta en una guía de opinión larga, el siguiente es un esquema de URI que se ajusta a un enfoque de gestión de recursos:

  • %código%
    • /tasks enumera todas las tareas, paginadas
    • GET agrega una sola tarea
  • %código%
    • POST responde con el objeto de estado de una sola tarea
    • /tasks/task/[id] cancela / elimina una tarea
  • %código%
    • GET enumera todos los grupos de tareas, paginados
    • DELETE agrega un grupo de tareas
  • %código%
    • /tasks/groups responde con el estado de un grupo de tareas
    • GET cancela / elimina el grupo de tareas

Esta estructura habla de recursos, no de qué hacer con ellos. Lo que se está haciendo con los recursos es la preocupación de otro servicio.

Otro punto importante que se debe hacer es que es recomendable no bloquear durante mucho tiempo en un controlador de solicitudes HTTP. Al igual que la interfaz de usuario, una interfaz HTTP debe ser receptiva, en una escala de tiempo que es algunos órdenes de magnitud más lenta (porque esta capa se ocupa de IO).

Es probable que avanzar hacia el diseño de una interfaz HTTP que gestione estrictamente los recursos sea tan difícil como alejar el trabajo de un hilo de la interfaz de usuario cuando se hace clic en un botón. Requiere que el servidor HTTP se comunique con otros servicios para ejecutar tareas en lugar de ejecutarlas en el controlador de solicitudes. Esto no es una implementación superficial, es un cambio de dirección.

Algunos ejemplos de cómo se usaría tal esquema de URI

Ejecución de una sola tarea y seguimiento del progreso:

  • POST con la tarea a ejecutar
    • /tasks/groups/group/[id] hasta que el objeto de respuesta GET tenga un valor positivo al tiempo que muestra el estado / progreso actual

Ejecutando una sola tarea y esperando su finalización:

  • DELETE con la tarea a ejecutar
    • POST /tasks hasta que GET /tasks/task/[id] tenga un valor positivo (es probable que tenga un tiempo de espera, por lo que este debe ser un bucle)

Ejecución de un grupo de tareas y seguimiento del progreso:

  • completed con el grupo de tareas a ejecutar
    • POST /tasks hasta que la propiedad de objeto de respuesta GET /tasks/task/[id]?awaitCompletion=true tenga valor, mostrando el estado de la tarea individual (3 tareas completadas de 5, por ejemplo)

Solicitando una ejecución para un grupo de tareas y esperando su finalización:

  • completed con el grupo de tareas a ejecutar
    • POST /tasks/groups hasta que responda con un resultado que denota la finalización (es probable que tenga un tiempo de espera, por lo que debería estar en un bucle)
respondido por el Ben Barkay 30.08.2016 - 12:22
80

Mi voto sería dividir estas tareas en solicitudes separadas. Sin embargo, si demasiados viajes de ida y vuelta son una preocupación, me topé con código de respuesta HTTP 207 - Estado múltiple

Copiar / pegar desde este enlace:

  

Una respuesta de estado múltiple transmite información sobre múltiples recursos en situaciones en las que varios códigos de estado pueden ser apropiados. El cuerpo de respuesta de estado múltiple predeterminado es una entidad HTTP text / xml o application / xml con un elemento raíz 'multistatus'. Otros elementos contienen códigos de estado de las series 200, 300, 400 y 500 generados durante la invocación del método. Los códigos de estado de la serie 100 NO DEBERÍAN registrarse en un elemento XML de "respuesta".

     

Aunque se utiliza '207' como el código de estado de respuesta general, el destinatario debe consultar el contenido del cuerpo de respuesta de varios estados para obtener más información sobre el éxito o el fracaso de la ejecución del método. La respuesta PUEDE usarse en casos de éxito, éxito parcial y también en situaciones de falla.

    
respondido por el neilsimp1 29.08.2016 - 14:54
23

Aunque el estado múltiple es una opción, devolvería 200 (Todo está bien) si todas las solicitudes tuvieron éxito y un error (500 o tal vez 207) de lo contrario.

El caso estándar normalmente debería ser 200 - todo funciona. Y los clientes solo deberían tener que verificar eso. Y solo si el error ocurrió, puedes devolver un 500 (o un 207). Creo que el 207 es una opción válida en el caso de al menos un error, pero si ve el paquete completo como una transacción también podría enviar 500. - El cliente querrá interpretar el mensaje de error de cualquier manera.

¿Por qué no enviar siempre 207? - Porque los casos estándar deberían ser fáciles y estándar. Mientras que los casos excepcionales pueden ser excepcionales. Un cliente solo debería tener que leer el cuerpo de respuesta y tomar decisiones más complejas, si una situación excepcional lo justifica.

    
respondido por el Falco 29.08.2016 - 16:10
13

Una opción sería devolver siempre un código de estado 200 y luego devolver errores específicos en el cuerpo de su documento JSON. Así es exactamente cómo se diseñan algunas API (siempre devuelven un código de estado 200 y envían el error en el cuerpo). Para obtener más detalles sobre los diferentes enfoques, consulte enlace

    
respondido por el BigONotation 30.08.2016 - 02:05
4

Creo que neilsimp1 es correcto, pero recomendaría un rediseño de los datos que se envían de tal manera que pueda enviar un 206 - Accepted y procesar los datos más tarde. Tal vez con devoluciones de llamada.

El problema al intentar enviar varias acciones en una sola solicitud es exactamente el hecho de que cada acción debe tener su propio "estado"

Mirando la importación de un CSV (no sé realmente de qué se trata el OP, pero es una versión simple). POSTE el CSV y recupere un 206. Luego, más adelante, el CSV se puede importar y puede obtener el estado de la importación con un GET (200) en una URL que muestra los errores por fila.

POST /imports/ -> 206
GET  /imports/1 -> 200
GET  /imports/1/errors -> 200 -> Has a list of errors

Este mismo patrón se puede aplicar a muchas operaciones por lotes

POST /operations/ -> 206
GET  /operations/1 -> 200
GET  /operations/1/errors -> 200 - > Has a list of errors.

El código que maneja la POST solo necesita verificar que el formato de los datos de las operaciones sea válido. Luego, en algún momento posterior, las operaciones pueden ser ejecutadas. En un trabajador de fondo, por lo que puede escalar más fácil, por ejemplo. Luego, puede verificar el estado de las operaciones cuando lo desee. Puede utilizar el sondeo o devoluciones de llamadas, o flujos o lo que sea para abordar la necesidad de saber cuándo se completa un conjunto de operaciones.

    
respondido por el coteyr 30.08.2016 - 01:26
1

Ya hay muchas buenas respuestas aquí, pero falta un aspecto:

¿Cuál es el contrato que esperan sus clientes?

Los códigos de retorno HTTP deben indicar al menos una distinción de éxito / fracaso y, por lo tanto, desempeñar el papel de "excepciones del hombre pobre". Luego, 200 significa "contrato cumplido por completo", y 4xx o 5xx indican un incumplimiento.

De manera ingenua, esperaría que el contrato de su solicitud de acciones múltiples sea "hacer todas mis tareas", y si una de ellas falla, entonces la solicitud no fue (completamente) exitosa. Por lo general, como cliente entendería 200 como "todo bien" y los códigos de las 400 y 500 familias me obligan a pensar en las consecuencias de una falla (parcial). Por lo tanto, use 200 para "todas las tareas realizadas" y 500 más una respuesta descriptiva en caso de una falla parcial.

Un contrato hipotético diferente podría ser "intentar hacer todas las acciones". Entonces está completamente en línea con el contrato si (algunas de) las acciones fallan. Por lo tanto, siempre devolverá 200 más un documento de resultados donde encontrará la información de éxito / fracaso para las tareas individuales.

Entonces, ¿cuál es el contrato que quieres seguir? Ambos son válidos, pero el primero (200 solo en caso de que se haya hecho todo) es más intuitivo para mí, y está mejor en línea con los patrones de software típicos. Y para la mayoría de los casos (con suerte) donde el servicio completó todas las tareas, es sencillo para el cliente detectar ese caso.

Un último aspecto importante: ¿Cómo comunica la decisión de su contrato a sus clientes? P.ej. en Java, usaría nombres de métodos como "doAll ()" o "tryToDoAll ()". En HTTP, puede asignar un nombre a las URL de punto final en consecuencia, con la esperanza de que los desarrolladores de sus clientes vean, lean y entiendan la denominación (no apostaría por eso). Una razón más para elegir el contrato de menor sorpresa.

    
respondido por el Ralf Kleberhoff 10.09.2018 - 20:58
0

Respuesta:

  

Solo use una solicitud por acción y acepte la sobrecarga adicional.

Un código de estado describe el estado de una operación. Por lo tanto, tiene sentido tener una operación por solicitud.

Múltiples operaciones independientes rompen el principio en el que se basan el modelo de solicitud-respuesta y los códigos de estado. Estás luchando contra la naturaleza.

HTTP / 1.1 y HTTP / 2 han hecho que la sobrecarga de solicitudes HTTP sea mucho menor. Estimo que hay muy pocas situaciones en las que es aconsejable agrupar solicitudes independientes.

Dicho esto,

(1) Puede realizar varias modificaciones con una solicitud de PATCH ( RFC 5789 ). Sin embargo, esto requiere que los cambios no sean independientes; se aplican atómicamente (todo o nada).

(2) Otros han señalado el código de estado múltiple 207. Sin embargo, esto se define solo para WebDAV ( RFC 4918 ), una extensión de HTTP.

  

El código de estado 207 (estado múltiple) proporciona el estado para múltiples      operaciones independientes (consulte la Sección 13 para obtener más información).

     

...

     

Una respuesta de varios estados transmite información sobre múltiples recursos      en situaciones donde múltiples códigos de estado pueden ser apropiados. La raíz 'multistatus'      El elemento [XML] contiene cero o más elementos de 'respuesta'      en cualquier orden, cada uno con información sobre un recurso individual.

Una respuesta 207 de WebDAV XML sería tan extraña como un pato en una API que no sea WebDAV. No hagas esto.

    
respondido por el Paul Draper 29.08.2016 - 20:37
0

Si realmente necesita tener varias acciones en una solicitud, ¿por qué no envolver todas las acciones en una transacción en el servidor? De esa manera, o todos triunfan o todos fallan.

Como cliente que usa la API, puedo lidiar con el éxito o el fracaso completo en una llamada a la API. Es difícil lidiar con el éxito parcial, ya que tendría que manejar todos los estados resultantes posibles.

    
respondido por el Dean 29.08.2016 - 21:23

Lea otras preguntas en las etiquetas