Código de status HTTP para "Processamento contínuo"

38

Estou criando uma API RESTful que suporta o enfileiramento de tarefas de longa duração para o manuseio eventual.

O fluxo de trabalho típico para esta API seria:

  1. O usuário preenche o formulário
  2. Dados de publicações do cliente na API
  3. retornos da API 202 aceitos
  4. O cliente redireciona o usuário para um URL exclusivo para essa solicitação ( /results/{request_id} )
  5. ~ eventualmente ~
  6. 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, faço uma solicitação para minha API ( GET /api/results/{request_id} ). O ideal é que a tarefa já tenha sido concluída até o momento, e eu retornaria um 200 OK com os resultados da tarefa deles.

Mas os usuários são intrometidos e esperamos muitas atualizações excessivamente zelosas, quando o resultado ainda não terminou de ser processado.

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 passar 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 solicitação GET , portanto, nada está sendo possivelmente "aceito". Isso seria uma escolha razoável?

A alternativa óbvia para tudo isso - que funciona, mas vence uma finalidade dos códigos de status - seria incluir sempre os metadados:

200 OK

{
    status: "complete",
    data: {
        foo: "123"
    }
}

... ou ...

200 OK

{
    status: "pending"
}

Em seguida, do lado do cliente, eu suspiro (%)switch on response.data.status para determinar se a solicitação foi concluída.

É isso que eu deveria estar fazendo? Ou existe uma alternativa melhor? Isso parece tão Web 1.0 para mim.

    
por Matthew Haugen 19.04.2016 / 19:46
fonte

4 respostas

40

HTTP 202 Aceito (HTTP / 1.1)

Você está procurando pelo status HTTP 202 Accepted . Consulte RFC 2616 :

The request has been accepted for processing, but the processing has not been completed.

Processamento HTTP 102 (WebDAV)

RFC 2518 sugere o uso de HTTP 102 Processing :

The 102 (Processing) status code is an interim response used to inform the client that the server has accepted the complete request, but has not yet completed it.

mas tem uma ressalva:

The server MUST send a final response after the request has been completed.

Não sei como interpretar a última frase. O servidor deve evitar enviar qualquer coisa durante o processamento e responder apenas após a conclusão? Ou apenas obriga a terminar a resposta apenas quando o processamento termina? Isso pode ser útil se você quiser relatar o progresso. Envie HTTP 102 e libere o byte de resposta por 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) sabe que deve esperar exatamente 100 caracteres, pode 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 seria exibida ao usuário, para que o usuário final possa saber como o processo está indo.

Problemas com descarga progressiva

Note que, embora essa técnica tenha seus méritos, eu não a recomendaria . Uma das razões é que isso força a conexão a permanecer em aberto, o que poderia prejudicar em termos de disponibilidade de serviço e não se adapta bem.

Uma abordagem melhor é responder com HTTP 202 Accepted e permitir que o usuário retorne mais tarde para determinar se o processamento foi encerrado (por exemplo, chamando repetidamente uma determinada URI, como /process/result , que responderia com HTTP 404 Não encontrado ou HTTP 409 Conflito até que o processo seja concluído e o resultado esteja 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 é:

POST /video/convert

que pega um arquivo de vídeo da requisição HTTP e faz alguma mágica com ele. Vamos imaginar que a magia é intensiva na CPU, por isso não pode ser feita em tempo real durante a transferência da solicitação. Isso significa que, assim que o arquivo for transferido, o servidor responderá com HTTP 202 Accepted com algum conteúdo JSON, significando “Sim, recebi seu vídeo e estou trabalhando nele; ele estará pronto em algum lugar no futuro e estará disponível por meio do ID 123. ”

O cliente tem a possibilidade de se inscrever em uma fila de mensagens para ser notificado quando o processamento for concluído. Uma vez concluído, o cliente pode baixar o vídeo processado em:

GET /video/download/123

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á com HTTP 404 , pois, de fato, o vídeo ainda não existe. Pode estar atualmente preparado. Talvez nunca tenha 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 houvesse serviço de fila de mensagens ou mecanismo similar)?

Nesse caso, você pode usar outro endpoint:

GET /video/status/123

que resultaria em uma resposta semelhante a esta:

HTTP 200
{
    "id": 123,
    "status": "queued",
    "priority": 2,
    "progress-percent": 0,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

Fazer a solicitação repetidamente mostrará o progresso até que seja:

HTTP 200
{
    "id": 123,
    "status": "done",
    "progress-percent": 100,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

É crucial fazer a diferença entre esses três tipos de solicitações:

  • POST /video/convert enfileira uma tarefa. Ele deve ser chamado apenas uma vez: chamá-lo novamente enfileiraria uma tarefa adicional.
  • GET /video/download/123 diz respeito ao resultado da operação: o recurso é o vídeo. O processamento - isso é 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 ou várias vezes.
  • GET /video/status/123 diz respeito ao processamento per se . Não enfileira nada. Não se importa com o vídeo resultante. O recurso é o próprio processamento. Pode ser chamado uma ou várias vezes.
por 19.04.2016 / 20:07
fonte
3

The obvious alternative to all this -- which functions, but defeats one purpose of status codes -- would be to always include the metadata:

Este é o caminho correto a seguir. O estado em que um recurso está em relação ao log específico do domínio (também conhecido como lógica de negócios) é uma questão para o tipo de conteúdo da representação do recurso.

Existem dois conceitos de diferença sendo confundidos aqui 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 do negócio entenda os diferentes estados desse recurso. O último não tem nada a ver com os 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ê usa GET um recurso seu cliente está pedindo ao servidor uma representação de um recurso no estado atual em que ele se encontra. Pode ser uma figura de um pássaro, pode ser um documento do Word, pode ser a temperatura externa atual . O protocolo HTTP não se importa. O código de status HTTP corresponde ao resultado dessa solicitação. O POST do cliente para o servidor transferiu um recurso para o servidor, onde o servidor deu a ele uma URL que o cliente pode visualizar? Sim? Então essa é uma resposta 201 Created .

O recurso pode ser uma reserva de linha aérea que está atualmente no estado "a ser revisado". Ou pode ser um pedido de compra de produto no estado "aprovado". Esses estados são específicos do domínio e não o que é o protocolo HTTP. O protocolo HTTP lida com a transferência de recursos entre o cliente e o servidor.

O ponto de REST e HTTP é que os protocolos não se preocupam com os detalhes dos recursos. Isso é feito de propósito, não se preocupa com os problemas específicos do domínio para que possa ser usado sem precisar saber nada sobre os problemas específicos do domínio. Você não reinterpreta o que os códigos de status HTTP significam em cada contexto diferente (um sistema de reserva de linha aérea, um sistema de processamento imagine, 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 do recurso. O protocolo HTTP é agnóstico para isso.

Quanto ao modo como o cliente descobre que o recurso Solicitar mudou de estado, a pesquisa é sua melhor aposta, pois mantém o controle no cliente e não assume uma conexão ininterrupta. Particularmente se vai ser potencialmente horas até que o estado mude. Mesmo se você disse para o inferno com o REST você está apenas mantendo a conexão aberta, mantendo-a aberta por horas e assumindo que nada vai dar errado seria uma má ideia. E se o usuário fechar o cliente ou a rede acabar? Se a granularidade for horas, o cliente pode solicitar o estado a cada poucos minutos até que a solicitação mude de "pendente" para "concluído".

Espero que ajude a esclarecer as coisas

    
por 21.04.2016 / 17:48
fonte
2

Código de status HTTP para recursos ainda não disponíveis sugere o retorno de 409 respostas a conflitos, em vez de uma resposta 404, no caso que um recurso não exista porque está no meio de ser gerado.

Da especificação w3 :

10.4.10 409 Conflict

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough

information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.

Isso é um pouco estranho, já que o código 409 "só é permitido 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 correspondente ao restante de sua API), como: "Este recurso está sendo gerado no momento. Foi iniciado às [TIME] e é estimado como concluído às [TIME]. tente novamente mais tarde. "

Observe que sugiro 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. Usuários não envolvidos com a geração do recurso achariam um erro 404 menos confuso.

    
por 20.04.2016 / 18:55
fonte
2

Achei as sugestões deste blog razoáveis: REST e trabalhos de longa duração .

Para resumir:

  1. Código de devolução de servidor "202 Aceito" com o cabeçalho "Local" definido para um URI para o cliente verificar o status, por exemplo, "/ queue / 12345".
  2. Até que o processamento seja concluído, o servidor responderá às consultas de status com "200 OK" e alguns dados de resposta mostrando o status do trabalho.
  3. Depois que o processamento é concluído, o servidor responde às consultas de status com "303 Ver outro" e "Local" contendo URI para o resultado final.
por 16.11.2018 / 18:58
fonte

Tags