Estoy construyendo una API RESTful que admite tareas de larga duración en cola para un manejo eventual.
El flujo de trabajo típico para esta API sería:
- El usuario completa el formulario
- El cliente publica datos en la API
- API devuelve 202 Aceptado
- El cliente redirige al usuario a una URL única para esa solicitud (
/results/{request_id}
) - ~ eventualmente ~
- El cliente vuelve a visitar la URL y ve los resultados en esa página.
Mi problema está en el paso 6. Cada vez que un usuario visita la página, presento una solicitud a mi API ( GET /api/results/{request_id}
). Idealmente, la tarea ya se habrá completado, y devolvería un 200 OK con los resultados de su tarea.
Pero los usuarios son agresivos, y espero muchas actualizaciones demasiado entusiastas, cuando el resultado aún no ha terminado de procesarse.
¿Cuál es mi mejor opción para un código de estado que indique que:
- esta solicitud existe
- aún no está hecho,
- Pero tampoco ha fallado.
No espero que un solo código comunique todo eso, pero me gustaría algo que me permita pasar metadatos en lugar de que el cliente espere contenido.
Podría tener sentido devolver un 202, ya que eso no tendría otro significado aquí: es una GET
solicitud, por lo que posiblemente no se "acepte" nada. ¿Sería una elección razonable?
La alternativa obvia a todo esto, que funciona, pero derrota un propósito de los códigos de estado, sería incluir siempre los metadatos:
200 OK
{
status: "complete",
data: {
foo: "123"
}
}
...o...
200 OK
{
status: "pending"
}
Luego del lado del cliente, yo (suspiro) switch
en response.data.status
determinar si la solicitud se ha completado.
¿Es esto lo que debería estar haciendo? ¿O hay una mejor alternativa? Esto se siente tan Web 1.0 para mí.
POST
abierta la primera solicitud. El problema principal con los sondeos largos o los sockets web es que el usuario puede cerrar el navegador y volver. Podría abrirlos nuevamente en ese momento (y eso es lo que hago), pero parece más limpio tener una única API a la que llamar antes de abrir esos sockets, ya que es un problema extremo que surja ese problema.Respuestas:
HTTP 202 aceptado (HTTP / 1.1)
Estás buscando el
HTTP 202 Accepted
estado. Ver RFC 2616 :Procesamiento HTTP 102 (WebDAV)
RFC 2518 sugiere usar
HTTP 102 Processing
:pero tiene una advertencia:
No estoy seguro de cómo interpretar la última oración. ¿Debería el servidor evitar enviar algo durante el procesamiento y responder solo después de la finalización? ¿O solo obliga a finalizar la respuesta solo cuando finaliza el procesamiento? Esto podría ser útil si desea informar sobre el progreso. Envíe HTTP 102 y vacíe la respuesta byte por byte (o línea por línea).
Por ejemplo, para un proceso largo pero lineal, puede enviar cien puntos, enjuagando después de cada carácter. Si el lado del cliente (como una aplicación de JavaScript) sabe que debe esperar exactamente 100 caracteres, puede coincidir con una barra de progreso para mostrar al usuario.
Otro ejemplo se refiere a un proceso que consta de varios pasos no lineales. Después de cada paso, puede vaciar un mensaje de registro que eventualmente se mostrará al usuario, para que el usuario final pueda saber cómo va el proceso.
Problemas con el enrojecimiento progresivo
Tenga en cuenta que si bien esta técnica tiene sus méritos, no la recomendaría . Una de las razones es que obliga a que la conexión permanezca abierta, lo que podría perjudicar en términos de disponibilidad del servicio y no se escala bien.
Un mejor enfoque es responder
HTTP 202 Accepted
y dejar que el usuario se comunique contigo más tarde para determinar si el procesamiento finalizó (por ejemplo, llamando repetidamente a un URI dado, como el/process/result
que respondería con HTTP 404 no encontrado o HTTP 409 Conflict hasta el proceso finaliza y el resultado está listo), o notifique al usuario cuando se realiza el procesamiento si puede volver a llamar al cliente, por ejemplo, a través de un servicio de cola de mensajes ( ejemplo ) o WebSockets.Ejemplo práctico
Imagine un servicio web que convierte videos. El punto de entrada es:
que toma un archivo de video de la solicitud HTTP y hace algo de magia con él. Imaginemos que la magia requiere mucha CPU, por lo que no se puede hacer en tiempo real durante la transferencia de la solicitud. Esto significa que una vez que se transfiere el archivo, el servidor responderá con un
HTTP 202 Accepted
contenido JSON, lo que significa "Sí, obtuve su video y estoy trabajando en ello; estará listo en algún momento en el futuro y estará disponible a través de la ID 123 ".El cliente tiene la posibilidad de suscribirse a una cola de mensajes para recibir una notificación cuando finalice el procesamiento. Una vez finalizado, el cliente puede descargar el video procesado yendo a:
lo que lleva a un
HTTP 200
.¿Qué sucede si el cliente consulta este URI antes de recibir la notificación? Bueno, el servidor responderá
HTTP 404
ya que, de hecho, el video aún no existe. Puede estar actualmente preparado. Puede que nunca se haya solicitado. Puede existir en algún momento en el pasado y eliminarse más tarde. Lo único que importa es que el video resultante no está disponible.Ahora, ¿qué pasa si el cliente no solo se preocupa por el video final, sino también por el progreso (que sería aún más importante si no hay un servicio de cola de mensajes o algún mecanismo similar)?
En este caso, puede usar otro punto final:
lo que daría una respuesta similar a esta:
Hacer la solicitud una y otra vez mostrará el progreso hasta que sea:
Es crucial hacer una diferencia entre esos tres tipos de solicitudes:
POST /video/convert
pone en cola una tarea. Debería llamarse solo una vez: llamarlo nuevamente pondría en cola una tarea adicional.GET /video/download/123
se refiere al resultado de la operación: el recurso es el video. El procesamiento, que es lo que sucedió bajo el capó para preparar el resultado real antes de la solicitud e independientemente de la solicitud, es irrelevante aquí. Se puede llamar una o varias veces.GET /video/status/123
se refiere al procesamiento per se . No hace cola para nada. No le importa el video resultante. El recurso es el procesamiento en sí. Se puede llamar una o varias veces.fuente
GET
embargo, ¿tiene sentido un 202 en respuesta a un ? Esa es ciertamente la elección correcta para la inicialPOST
, por eso la estoy usando. Pero parece semánticamente sospechoso que unGET
decir "aceptado" cuando no acepta nada de esa solicitud en particular.POST
el trabajo a la cola y luegoGET
los resultados, posiblemente después de que el cliente haya cerrado la sesión. Un 404 es algo que también he considerado, pero parece incorrecto, ya que se encuentra la solicitud , simplemente no se ha completado. Eso me indicaría que no se encontró el trabajo en cola, que es una situación muy diferente.GET
parte, no pienses en ello como una solicitud incompleta , sino como una solicitud para obtener el resultado de la operación . Por ejemplo, si le digo que convierta un video y le tome cinco minutos hacerlo, solicitar un video convertido dos minutos después debería dar como resultado HTTP 404, porque el video simplemente todavía no está allí. Solicitar el progreso de la operación en sí, por otro lado, probablemente dará como resultado un HTTP 200 que contenga el número de bytes convertidos, la velocidad, etc.Este es el camino correcto a seguir. El estado en que se encuentran los recursos con respecto al registro específico del dominio (también conocido como lógica de negocios) depende del tipo de contenido de la representación del recurso.
Aquí se combinan dos conceptos diferentes que en realidad son diferentes. Uno es el estado de la transferencia de estado entre el cliente y el servidor de un recurso, y el otro es el estado del recurso en sí mismo en cualquier contexto en el que el dominio comercial asuma los diferentes estados de ese recurso. Esto último no tiene nada que ver con los códigos de estado HTTP.
Recuerde que los códigos de estado HTTP corresponden a la transferencia de estado entre el cliente y el servidor del recurso que se está tratando, independientemente de cualquier detalle de ese recurso. Cuando usted es
GET
un recurso, su cliente le pide al servidor una representación de un recurso en el estado actual en que se encuentra. Esa podría ser una imagen de un pájaro, podría ser un documento de Word, podría ser la temperatura externa actual. Al protocolo HTTP no le importa. El código de estado HTTP corresponde al resultado de esa solicitud. ¿TransmitióPOST
desde el cliente al servidor un recurso al servidor, donde el servidor le dio una URL que el cliente puede ver? ¿Si? Entonces esa es una201 Created
respuesta.El recurso podría ser una reserva de una aerolínea que se encuentra actualmente en el estado 'para ser revisado'. O podría ser una orden de compra del producto que se encuentra en el estado 'aprobado'. Esos estados son específicos del dominio y no de lo que trata el protocolo HTTP. El protocolo HTTP se ocupa de la transferencia de recursos entre el cliente y el servidor.
El punto de REST y HTTP es que los protocolos no se preocupan por los detalles de los recursos. Esto es a propósito, no se ocupa de los problemas específicos del dominio para que pueda usarse sin tener que saber nada sobre los problemas específicos del dominio. No reinterpreta lo que significan los códigos de estado HTTP en cada contexto diferente (un sistema de reserva de línea aérea, un sistema de procesamiento de imágenes, un sistema de seguridad de video, etc.).
El material específico del dominio es para que el cliente y el servidor se resuelvan entre sí en función
Content Type
del recurso. El protocolo HTTP es independiente de esto.En cuanto a cómo el cliente se da cuenta de que el recurso Solicitud ha cambiado de estado, la votación es su mejor opción ya que mantiene el control en el cliente y no asume una conexión ininterrumpida. Particularmente si van a pasar horas hasta que el estado cambie. Incluso si dijiste al infierno con REST, solo vas a mantener la conexión abierta, mantenerla abierta durante horas y asumir que nada saldrá mal sería una mala idea. ¿Qué pasa si el usuario cierra el cliente o se apaga la red? Si la granularidad es de horas, el cliente puede solicitar el estado cada pocos minutos hasta que la Solicitud cambie de "pendiente" a "finalizada".
Espero que ayude a aclarar las cosas.
fuente
Las sugerencias de este blog me parecieron razonables: REST y trabajos de larga duración .
Para resumir:
fuente
El código de estado HTTP para el recurso aún no disponible sugiere que se devuelva una respuesta de conflicto 409, en lugar de una respuesta 404, en el caso de que un recurso no exista porque se está generando.
De la especificación w3 :
Esto es un poco incómodo, ya que el código 409 "solo está permitido en situaciones en las que se espera que el usuario pueda resolver el conflicto y volver a enviar la solicitud". Sugiero que el cuerpo de la respuesta incluya un mensaje (posiblemente en algún formato de respuesta que coincida con el resto de su API) como "Este recurso se está generando actualmente. Se inició a las [TIME] y se estima que se completará a las [TIME]. Por favor inténtalo de nuevo más tarde ".
Tenga en cuenta que solo sugeriría el enfoque 409 si es muy probable que el usuario que solicita el recurso sea también el usuario que inició la generación de ese recurso. Los usuarios que no participan en la generación del recurso encontrarán un error 404 menos confuso.
fuente