Estou criando uma API RESTful que oferece suporte a tarefas de longa execução de enfileiramento para manipulação eventual.
O fluxo de trabalho típico para esta API seria:
- O usuário preenche o formulário
- O cliente publica dados na API
- A API retorna 202 Aceito
- O cliente redireciona o usuário para um URL exclusivo para essa solicitação (
/results/{request_id}
) - ~ eventualmente ~
- O cliente visita o URL novamente e vê os resultados nessa página.
Meu problema está na etapa 6. Sempre que um usuário visita a página, arquivo uma solicitação na minha API ( GET /api/results/{request_id}
). Idealmente, a tarefa já estará concluída e eu retornaria 200 OK com os resultados da tarefa.
Mas os usuários são insistentes e espero muitas atualizações excessivamente zelosas, quando o resultado ainda não tiver terminado o processamento.
Qual é a minha melhor opção para um código de status para indicar que:
- esta solicitação existe,
- ainda não está pronto,
- mas também não falhou.
Não espero que um único código comunique tudo isso, mas gostaria de algo que me permita transmitir metadados em vez de fazer com que o cliente espere conteúdo.
Poderia fazer sentido retornar um 202, já que isso não teria outro significado aqui: é uma GET
solicitação, portanto nada está sendo "aceito". Seria uma escolha razoável?
A alternativa óbvia a tudo isso - que funciona, mas derrota um objetivo dos códigos de status - seria sempre incluir os metadados:
200 OK
{
status: "complete",
data: {
foo: "123"
}
}
...ou...
200 OK
{
status: "pending"
}
Em seguida, do lado do cliente, gostaria de (suspiro) switch
em response.data.status
determinar se o pedido foi concluído.
É isso que eu deveria estar fazendo? Ou existe uma alternativa melhor? Isso parece tão Web 1.0 para mim.
POST
pedido em aberto. O principal problema com pesquisas longas ou soquetes da Web é que o usuário pode fechar o navegador e voltar. Eu poderia abri-los novamente naquele momento (e é isso que eu faço), mas parece mais limpo ter uma única API para chamar antes de abrir esses soquetes, já que é um caso delicado que esse problema ocorra.Respostas:
HTTP 202 aceito (HTTP / 1.1)
Você está procurando
HTTP 202 Accepted
status. Veja RFC 2616 :Processamento HTTP 102 (WebDAV)
A RFC 2518 sugere o uso de
HTTP 102 Processing
:mas tem uma ressalva:
Não sei como interpretar a última frase. O servidor deve evitar enviar algo durante o processamento e responder somente após a conclusão? Ou apenas força o encerramento da resposta somente quando o processamento termina? Isso pode ser útil se você deseja relatar o progresso. Envie HTTP 102 e libere resposta byte a byte (ou linha por linha).
Por exemplo, para um processo longo mas linear, você pode enviar cem pontos, liberando após cada caractere. Se o lado do cliente (como um aplicativo JavaScript) souber que deve esperar exatamente 100 caracteres, poderá combiná-lo com uma barra de progresso para mostrar ao usuário.
Outro exemplo diz respeito a um processo que consiste em várias etapas não lineares. Após cada etapa, você pode liberar uma mensagem de log que eventualmente será exibida ao usuário, para que o usuário final saiba como está o processo.
Problemas com descarga progressiva
Observe que, embora essa técnica tenha seus méritos, eu não a recomendaria . Uma das razões é que força a conexão a permanecer aberta, o que pode prejudicar em termos de disponibilidade do serviço e não é bem dimensionado.
Uma abordagem melhor é responder
HTTP 202 Accepted
e permitir que o usuário retorne mais tarde para determinar se o processamento terminou (por exemplo, chamando repetidamente um determinado URI, como o/process/result
que responderia com o HTTP 404 Not Found ou o HTTP 409 Conflict até o processo termina e o resultado está pronto) ou notifique o usuário quando o processamento estiver concluído, se você puder chamar o cliente de volta, por exemplo, por meio de um serviço de fila de mensagens ( exemplo ) ou WebSockets.Exemplo prático
Imagine um serviço da web que converte vídeos. O ponto de entrada é:
que pega um arquivo de vídeo da solicitação HTTP e faz alguma mágica com ele. Vamos imaginar que a mágica consome muita CPU, portanto não pode ser feita em tempo real durante a transferência da solicitação. Isso significa que, depois que o arquivo for transferido, o servidor responderá com um
HTTP 202 Accepted
conteúdo JSON, o que significa “Sim, peguei o seu vídeo e estou trabalhando nele; estará pronto em algum lugar no futuro e estará disponível através do ID 123. "O cliente tem a possibilidade de se inscrever em uma fila de mensagens para ser notificado quando o processamento terminar. Quando terminar, o cliente pode baixar o vídeo processado, acessando:
o que leva a um
HTTP 200
.O que acontece se o cliente consultar esse URI antes de receber a notificação? Bem, o servidor responderá uma
HTTP 404
vez que, de fato, o vídeo ainda não existe. Pode estar preparado atualmente. Pode nunca ter sido solicitado. Pode existir algum tempo no passado e ser removido mais tarde. Tudo o que importa é que o vídeo resultante não esteja disponível.Agora, e se o cliente se importar não apenas com o vídeo final, mas também com o progresso (o que seria ainda mais importante se não houver serviço de fila de mensagens ou mecanismo semelhante)?
Nesse caso, você pode usar outro ponto de extremidade:
o que resultaria em uma resposta semelhante a esta:
Fazer a solicitação repetidamente mostrará o progresso até que seja:
É crucial fazer a diferença entre esses três tipos de solicitações:
POST /video/convert
enfileira uma tarefa. Deve ser chamado apenas uma vez: chamá-lo novamente colocaria em fila uma tarefa adicional.GET /video/download/123
diz respeito ao resultado da operação: o recurso é o vídeo. O processamento - foi o que aconteceu sob o capô para preparar o resultado real antes da solicitação e independentemente da solicitação - é irrelevante aqui. Pode ser chamado uma vez ou várias vezes.GET /video/status/123
diz respeito ao processamento em si . Não enfileira nada. Não se importa com o vídeo resultante. O recurso é o próprio processamento. Pode ser chamado uma vez ou várias vezes.fonte
GET
? Essa é certamente a escolha correta para a inicialPOST
, e é por isso que a estou usando. Mas parece semanticamente suspeito que alguémGET
diga "aceito" quando não aceita nada desse pedido em particular.POST
o trabalho para a fila de espera, depoisGET
os resultados, potencialmente depois que o cliente encerra a sessão. Um 404 é algo que eu considerei também, mas parece errado, desde que a solicitação foi encontrada, ela simplesmente não foi concluída. Isso indicaria para mim que o trabalho na fila não foi encontrado, o que é uma situação muito diferente.GET
parte, não pense nisso como uma solicitação incompleta , mas como uma solicitação para obter o resultado da operação . Por exemplo, se eu lhe disser para converter um vídeo e você levar cinco minutos para fazê-lo, solicitar um vídeo convertido dois minutos depois deve resultar em HTTP 404, porque o vídeo simplesmente ainda não está lá. Requerente para o progresso da operação propriamente dita, por outro lado, irá provavelmente resultar em um HTTP 200 que contém o número de bytes convertido, a velocidade, etcEste é o caminho correto a seguir. O estado em que os recursos estão em relação ao log específico do domínio (também conhecido como lógica comercial) é uma questão para o tipo de conteúdo da representação do recurso.
Existem aqui dois conceitos diferentes que são realmente diferentes. Um é o status da transferência de estado entre o cliente e o servidor de um recurso e o outro é o estado do próprio recurso, em qualquer contexto em que o domínio de negócios entenda os diferentes estados desse recurso. O último não tem nada a ver com códigos de status HTTP.
Lembre-se de que os códigos de status HTTP correspondem à transferência de estado entre o cliente e o servidor do recurso que está sendo tratado, independentemente de quaisquer detalhes desse recurso. Quando você
GET
utiliza um recurso, seu cliente está solicitando ao servidor a representação de um recurso no estado atual em que está. Isso pode ser uma imagem de um pássaro, um documento do Word, a temperatura externa atual. O protocolo HTTP não se importa. O código de status HTTP corresponde ao resultado dessa solicitação. OPOST
do cliente para o servidor transferiu um recurso para o servidor, onde o servidor forneceu uma URL que o cliente pode exibir? Sim? Então isso é uma201 Created
resposta.O recurso pode ser uma reserva de companhia aérea que está atualmente no estado 'a ser revisado'. Ou pode ser um pedido de compra de produto que está no estado 'aprovado'. Esses estados são específicos do domínio e não são sobre o que é o protocolo HTTP. O protocolo HTTP lida com a transferência de recursos entre cliente e servidor.
O ponto do REST e HTTP é que os protocolos não se preocupam com os detalhes dos recursos. Isso é proposital, não se preocupa com os problemas específicos do domínio, para que possa ser usado sem ter que saber nada sobre os problemas específicos do domínio. Você não reinterpreta o que significam os códigos de status HTTP em cada contexto diferente (um sistema de reservas de companhias aéreas, um sistema de processamento de imagens, um sistema de segurança de vídeo etc.).
O material específico do domínio é para o cliente e o servidor descobrirem entre si com base no
Content Type
recurso. O protocolo HTTP é independente disso.Quanto à forma como o cliente descobre que o recurso Request mudou de estado, a pesquisa é a melhor opção, pois mantém o controle no cliente e não assume conexão ininterrupta. Especialmente se for potencialmente horas até que o estado mude. Mesmo que você tenha dito ao inferno com o REST, você manterá a conexão aberta, mantê-la aberta por horas e assumindo que nada dará errado seria uma má idéia. E se o usuário fechar o cliente ou a rede sair. Se a granularidade for horas, o cliente poderá solicitar o estado a cada poucos minutos até que a Solicitação seja alterada de 'pendente' para 'concluída'.
Espero que ajude a esclarecer as coisas
fonte
Eu achei as sugestões deste blog razoáveis: REST e trabalhos de longa duração .
Para resumir:
fonte
O código de status HTTP para o recurso ainda não disponível sugere retornar uma resposta de conflito 409, em vez de uma resposta 404, no caso de um recurso não existir porque está no meio da geração.
A partir da especificação w3 :
Isso é um pouco estranho, pois o código 409 "é permitido apenas em situações em que se espera que o usuário possa resolver o conflito e reenviar a solicitação". Sugiro que o corpo da resposta inclua uma mensagem (possivelmente em algum formato de resposta que corresponda ao restante da sua API) como "Este recurso está sendo gerado no momento. Foi iniciado em [TIME] e estima-se que seja concluído em [TIME]. Por favor, tente mais tarde."
Observe que eu sugeriria apenas a abordagem 409 se for altamente provável que o usuário que está solicitando o recurso também seja o usuário que iniciou a geração desse recurso. Os usuários não envolvidos na geração do recurso considerariam um erro 404 menos confuso.
fonte