Estou criando uma API em que o usuário pode solicitar ao servidor que execute várias ações em uma solicitação HTTP. O resultado é retornado como uma matriz JSON, com uma entrada por ação.
Cada uma dessas ações pode falhar ou ter sucesso independentemente uma da outra. Por exemplo, a primeira ação pode ter êxito, a entrada para a segunda ação pode estar mal formatada e falhar na validação e a terceira ação pode causar um erro inesperado.
Se houvesse uma solicitação por ação, eu retornaria os códigos de status 200, 422 e 500, respectivamente. Mas agora, quando há apenas uma solicitação, qual código de status devo retornar?
Algumas opções:
- Sempre retorne 200 e forneça informações mais detalhadas no corpo.
- Talvez siga a regra acima apenas quando houver mais de uma ação na solicitação?
- Talvez retorne 200 se todas as solicitações forem bem-sucedidas, caso contrário 500 (ou algum outro código)?
- Basta usar uma solicitação por ação e aceitar a sobrecarga extra.
- Algo completamente diferente?
Respostas:
A resposta curta e direta
Como a solicitação fala da execução da lista de tarefas (as tarefas são o recurso de que estamos falando aqui), se o grupo de tarefas foi movido para a execução (ou seja, independentemente do resultado da execução), seria sensato que o status da resposta será
200 OK
. Caso contrário, se houver um problema que impeça a execução do grupo de tarefas, como falha na validação dos objetos da tarefa ou algum serviço necessário não esteja disponível, por exemplo, o status da resposta deverá indicar esse erro. Depois que, quando a execução das tarefas começa, visto que as tarefas a serem executadas estão listadas no corpo da solicitação, eu esperaria que os resultados da execução fossem listados no corpo da resposta.A resposta longa e filosófica
Você está enfrentando esse dilema porque está se desviando para o que o HTTP foi projetado. Você não está interagindo para gerenciar recursos; pelo contrário, está usando-o como meio de invocação remota de método (o que não é muito estranho, mas funciona mal sem um esquema pré-concebido).
Com o exposto acima, e sem coragem de transformar essa resposta em um longo guia opinativo, a seguir é apresentado um esquema de URI compatível com uma abordagem de gerenciamento de recursos:
/tasks
GET
lista todas as tarefas, paginadasPOST
adiciona uma única tarefa/tasks/task/[id]
GET
responde com o objeto de estado de uma única tarefaDELETE
cancela / exclui uma tarefa/tasks/groups
GET
lista todos os grupos de tarefas, paginadosPOST
adiciona um grupo de tarefas/tasks/groups/group/[id]
GET
responde com o estado de um grupo de tarefasDELETE
cancela / exclui o grupo de tarefasEssa estrutura fala sobre recursos, não o que fazer com eles. O que está sendo feito com recursos é a preocupação de outro serviço.
Outro ponto importante a destacar é que é aconselhável não bloquear por muito tempo em um manipulador de solicitações HTTP. Assim como a interface do usuário, uma interface HTTP deve ser responsiva - em uma escala de tempo que é algumas ordens de magnitude mais lenta (porque essa camada lida com E / S).
Tomar a decisão de projetar uma interface HTTP que gerencia estritamente os recursos provavelmente é tão difícil quanto afastar o trabalho de um thread da interface do usuário quando um botão é clicado. Requer que o servidor HTTP se comunique com outros serviços para executar tarefas, em vez de executá-las no manipulador de solicitações. Esta não é uma implementação superficial, é uma mudança de direção.
Alguns exemplos de como esse esquema de URI seria usado
Executando uma única tarefa e acompanhando o progresso:
POST /tasks
com a tarefa de executarGET /tasks/task/[id]
até que o objeto de respostacompleted
tenha um valor positivo enquanto mostra o status / progresso atualExecutando uma única tarefa e aguardando sua conclusão:
POST /tasks
com a tarefa de executarGET /tasks/task/[id]?awaitCompletion=true
até quecompleted
tenha valor positivo (provavelmente tem tempo limite, motivo pelo qual isso deve ser repetido)Executando um grupo de tarefas e acompanhando o progresso:
POST /tasks/groups
com o grupo de tarefas para executarGET /tasks/groups/group/[groupId]
até que acompleted
propriedade do objeto de resposta tenha valor, mostrando o status da tarefa individual (3 tarefas concluídas em 5, por exemplo)Solicitando uma execução para um grupo de tarefas e aguardando sua conclusão:
POST /tasks/groups
com o grupo de tarefas para executarGET /tasks/groups/group/[groupId]?awaitCompletion=true
até responder com resultado que indica conclusão (provavelmente tem tempo limite, e é por isso que deve ser feito um loop)fonte
Meu voto seria dividir essas tarefas em solicitações separadas. No entanto, se há muitas viagens de ida e volta, encontrei o código de resposta HTTP 207 - Multi-Status
Copie / cole a partir deste link:
fonte
207
parece ser o que o OP quer, mas eu realmente quero enfatizar que provavelmente é uma má idéia ter essa abordagem de solicitação múltipla em um. Se a preocupação é o desempenho, então você deve ser arquitetar para estilo de nuvem sistemas escalável horizontalmente (que é algo que os sistemas baseados em HTTP são grandes em)Embora o multi-status seja uma opção, eu retornaria 200 (Tudo está bem) se todas as solicitações fossem bem-sucedidas e um erro (500 ou talvez 207) caso contrário.
O caso padrão normalmente deve ser 200 - tudo funciona. E os clientes só precisam verificar isso. E somente se o caso de erro aconteceu, você pode retornar um 500 (ou um 207). Eu acho que o 207 é uma opção válida no caso de pelo menos um erro, mas se você vir o pacote inteiro como uma transação, também poderá enviar 500. - O cliente desejará interpretar a mensagem de erro de qualquer maneira.
Por que nem sempre enviar 207? - Porque os casos padrão devem ser fáceis e padrão. Embora casos excepcionais possam ser excepcionais. Um cliente deve apenas ler o órgão de resposta e tomar decisões complexas adicionais, se uma situação excepcional o justificar.
fonte
Uma opção seria sempre retornar um código de status 200 e retornar erros específicos no corpo do documento JSON. É exatamente assim que algumas APIs são projetadas (elas sempre retornam um código de status 200 e enviam o erro no corpo). Para mais detalhes sobre as diferentes abordagens, consulte http://archive.oreilly.com/pub/post/restful_error_handling.html
fonte
200
para indicar que tudo está bem, a solicitação é recebida e válida e, em seguida, use o JSON para fornecer detalhes sobre o que aconteceu nessa solicitação (ou seja, o resultado das transações).Acho que o neilsimp1 está correto, mas eu recomendaria uma reformulação dos dados enviados de forma que você pudesse enviar
206 - Accepted
e processar os dados posteriormente. Talvez com retornos de chamada.O problema de tentar enviar várias ações em uma única solicitação é exatamente o fato de que cada ação deve ter seu próprio "status"
Olhando para importar um CSV (não sei realmente o que é o OP, mas é uma versão simples). POSTE o CSV e retorne um 206. Posteriormente, o CSV poderá ser importado e você poderá obter o status da importação com um GET (200) em um URL que mostra erros por linha.
Esse mesmo padrão pode ser aplicado a muitas opterações em lote
O código que manipula o POST precisa apenas verificar se o formato dos dados de operações é válido. Em algum momento posterior, as operações podem ser executadas. Em um trabalhador em segundo plano, para que você possa escalar com mais facilidade, por exemplo. Depois, você pode verificar o status das operações sempre que desejar. Você pode usar pesquisas ou retornos de chamada, fluxos ou qualquer outra coisa para atender à necessidade de saber quando um conjunto de operações é concluído.
fonte
Já existem muitas boas respostas aqui, mas um aspecto está faltando:
Qual é o contrato que seus clientes esperam?
Os códigos de retorno HTTP devem indicar pelo menos uma distinção de sucesso / falha e, assim, desempenhar o papel de "exceções do pobre homem". Então 200 significa "contrato completamente cumprido" e 4xx ou 5xx indicam falha no cumprimento.
Ingenuamente, eu esperaria que o contrato da sua solicitação de várias ações fosse "faça todas as minhas tarefas" e, se uma delas falhar, a solicitação não foi (completamente) bem-sucedida. Normalmente, como cliente, eu entendia 200 como "tudo bem" e os códigos das famílias 400 e 500 me forçam a pensar nas consequências de uma falha (parcial). Portanto, use 200 para "todas as tarefas concluídas" e 500, além de uma resposta descritiva em caso de falha parcial.
Um contrato hipotético diferente pode ser "tente executar todas as ações". Então, está completamente de acordo com o contrato se (algumas das) ações falharem. Portanto, você sempre retornaria 200 mais um documento de resultados, onde encontrará as informações de sucesso / falha para as tarefas individuais.
Então, qual é o contrato que você deseja seguir? Ambos são válidos, mas o primeiro (200 apenas no caso de tudo ter sido feito) é mais intuitivo para mim e melhor alinhado aos padrões típicos de software. E para a (espero) maioria dos casos em que o serviço concluiu todas as tarefas, é fácil o cliente detectar esse caso.
Um aspecto final importante: como você comunica sua decisão de contrato a seus clientes? Por exemplo, em Java, eu usaria nomes de métodos como "doAll ()" ou "tryToDoAll ()". No HTTP, você pode nomear os URLs dos terminais adequadamente, esperando que os desenvolvedores de seus clientes vejam, leiam e entendam a nomeação (eu não apostaria nisso). Mais um motivo para escolher o contrato de menor surpresa.
fonte
Responda:
Um código de status descreve o status de uma operação. Portanto, faz sentido ter uma operação por solicitação.
Várias operações independentes quebram o princípio no qual o modelo de solicitação-resposta e os códigos de status se baseiam. Você está lutando contra a natureza.
HTTP / 1.1 e HTTP / 2 reduziram muito as solicitações de HTTP. Eu estimo que há muito poucas situações em que é aconselhável agrupar solicitações independentes.
Dito isto,
(1) Você pode fazer várias modificações com uma solicitação PATCH ( RFC 5789 ). No entanto, isso requer que as alterações não sejam independentes; eles são aplicados atomicamente (tudo ou nada).
(2) Outros apontaram o código 207 Multi-Status. No entanto, isso é definido apenas para o WebDAV ( RFC 4918 ), uma extensão do HTTP.
Uma resposta de 207 WebDAV XML seria tão estranha quanto um pato em uma API não WebDAV. Não faça isso.
fonte
Se você realmente precisa ter várias ações em uma solicitação, por que não agrupar todas as ações em uma transação no back-end? Dessa forma, todos têm sucesso ou fracassam.
Como cliente que usa a API, posso lidar com sucesso ou falha completa em uma chamada de API. É difícil lidar com o sucesso parcial, pois eu teria que lidar com todos os possíveis estados resultantes.
fonte