HTTP Status Cheat Sheet – LuizTools
HTTP Status Cheat Sheet
Escrito por Luiz Duarte em 22/12/2020
Este artigo é abordado na prática nos meus cursos de Node.js!
Já tem alguns anos que trabalho forte com backend e ensino outros a fazê-lo e uma das dúvidas mais recorrentes é sobre o uso correto dos status code HTTP para retorno de mensagens em Web APIs RESTful.
Embora possa parecer muito tentador simplesmente retornar 200 pra tudo e um JSON no corpo detalhando se teve sucesso ou não e sua respectiva mensagem, isso não é nem de perto uma abordagem RESTful, vide que era exatamente o que se fazia no início dos anos 2000 com SOAP.
Então, o artigo de hoje é uma cheat sheet, ou um compilado de status codes comuns que uso e em que situações uso cada um deles. Alguns são extremamente diretos e sem muita margem de interpretação, outros acabam gerando algumas divergências entre devs.
Dentro do possível tentarei me ater à especificação oficial do HTTP, a RFC 7231 de 2014, a mais recente que encontrei e na documentação da Mozilla, que utilizo bastante como base. Ainda assim, caso tenha alguma dúvida ou discordância, deixe nos comentários.
Nesta primeira seção, quero apresentar os status code HTTP que uso para respostas de requisições bem sucedidas.
O mais clássico de todos, uso sempre que a requisição foi bem sucedida, o processamento foi concluída, não houve criação de novos recurso e há retorno no corpo da resposta. Pode ser usado por exemplo como resposta para consultas de registros, retornando-os no corpo.
É uma especialização do 200, onde neste caso, criamos um novo recurso no servidor, como por exemplo ao salvar um novo registro no banco. No corpo da requisição, devemos este registro recém criado.
Imagine que seu servidor receba a requisição mas que ele vá processá-la de maneira assíncrona, usando filas por exemplo. Você retorna para o cliente que recebeu com sucesso e está processando, mas não há retorno no corpo da resposta.
Este código indica que seu servidor processou a requisição com sucesso mas não há nada novo para ser devolvido no corpo da resposta. O exemplo clássico é se os seus PUTS e DELETEs não retornam nada, um 204 pode ser utilizado pra eles.
Nesta segunda seção, vou apresentar os status code HTTP que utilizo para mensagens de erro, usando as famílias 400 e 500.
O servidor não conseguiu processar a requisição devido a parâmetros inválidos ou errados nela (headers, path params e query string geralmente), geralmente atrelados a regras de negócio da aplicação. Caso o cliente deseje tentar novamente, ele deve modificar a requisição.
Você pode opcionalmente no corpo da resposta indicar o que o cliente errou (query string faltante, por exemplo), se não for expor informações sensíveis ou que possam ser usadas para explorar a segurança da sua API. Isso serve para ajudar ele a tentar novamente.
Diferente do 400 que indica que você deve tentar de novo mudando parâmetros, um erro 500 é devolvido quando um erro inesperado acontece no servidor, algo realmente não previsto como a captura de uma exceção ou um momento difícil do servidor em processar requisições. Também diferente do 400, não há qualquer garantia de que mudando path params, query string, etc o cliente conseguirá realizar nova requisição com sucesso.
Como o erro 500 é algo inesperado, não se recomenda devolver detalhes do erro no corpo da resposta, uma vez que pode expor vulnerabilidades da sua aplicação que nem você conhece. Sendo assim, registre o ocorrido com detalhes internamente, mas devolva um erro genérico ao cliente.
Um 401 é retornado sempre que uma requisição é enviada para uma rota segura sem informações de autenticação ou com informações inválidas/expiradas. Sem qualquer mensagem sendo necessária, isso já indica ao cliente que ele deve se autenticar primeiro e enviar nas próximas requests suas informações de segurança (geralmente um token JWT ou similar) no header da requisição.
Sob hipótese alguma dê detalhes do seu mecanismo de segurança aqui.
Um 403 deve ser retornado quando um usuário não possui permissão para acessar um recurso/rota da sua aplicação ou web API. Não importa se ele está autenticado ou não, é uma request proibida para ele.
Por exemplo, ele pode estar acessando informações de outro usuário manipulando a URL, tentando fazer uma tarefa não permitida para seu perfil de segurança ou tentando acessar pastas do servidor que estão fora dos limites da aplicação.
Por questões de segurança, não se recomenda dar detalhes no corpo do retorno, assim como no 401.
Um 404 deve ser enviado sempre que um recurso não for encontrado no servidor, como por exemplo uma busca por usuário com um ID inexistente. Como os browsers costumam jogar 404 naturalmente quando se tenta acessar URLs inválidas, pode ser que você queira retornar alguma mensagem mais detalhada no corpo da resposta, indicando se o recurso não existe mesmo, já que o padrão subentende-se que a URL não existe.
Existe uma discussão se em uma tentativa de login com um usuário inexistente se devemos retornar 401 ou 404. Para não dar detalhes do porque a autenticação falhou, o ideal é dar um 401 sempre.
Então veio uma requisição para salvar um cliente novo na base, mas devido a uma regra de negócio ele conflita com outro cliente já existente (CPF repetido talvez). Retornamos um 409 para ele, opcionalmente com detalhes do conflito no corpo da resposta.
Alguns devs usam 400 para estes conflitos, mas existe um código específico para eles, então é uma boa utilizá-lo.
Este é outro exemplo de status code que os devs costumam usar 400 por preguiça. Diferente do 400 BAD REQUEST que é mais genérico, que serve para requests com qualquer problema, o 422 é mais específico e trata de entidades enviadas junto da requisição, ou seja, no seu body. Esta entidade enviada não pode ser processada, geralmente devido a uma regra e negócio do servidor, como validação de campos, por exemplo.
Então, se você faz validação de entidades nas requests e uma request não passou na validação, retorne um 422 pra ela, geralmente com mais informações da negativa no corpo da resposta.
Este erro aqui é mais específico para web APIs que possuam limite de requests mensal, como SMTP Gateways e outros serviços populares que cobram por uso. Quando uma request de um cliente chega no servidor, mas a quota de uso do cliente já está estourada, deve-se retornar um 429.
Como é um recurso bem atrelado ao business da sua aplicação, pode ser interessante retornar mais detalhes no corpo da resposta, indicando qual recurso que ele exauriu o uso no mês e próximos passos se ele quiser pagar mais.
Vale aqui uma menção do porquê não utilizo outros códigos além destes.
Sobre os códigos da família 300, são todos relacionados a redirecionamentos. Quando faço esse tipo de processamento, geralmente a própria biblioteca que estou usando (como o Express no caso de Node.js) já retorna o status code de redirecionamento correto.
Sobre o 405 METHOD NOT ALLOWED e o 501 NOT IMPLEMENTED, geralmente quando não implementamos um método na Web API, e um usuário tenta usar, ele recebe um 404. Não faz muito sentido pra mim implementar uma rota apenas para retornar um 405. O mesmo vale para um 410 GONE, se o recurso não existe mais, vai receber um 404 na minha opinião (mesmo no caso de soft-delete).
408 REQUEST TIMEOUT é enviada automaticamente pelos servidores quando esgota o tempo da requisição ser processada, bem como o 413 PAYLOAD TOO LARGE quando o body está grande demais, o 414 URI TOO LONG quando a URL está exagerada, o 431 REQUEST HEADER FIELDS TOO LARGE quando é o header que está exagerado e o 503 SERVICE UNAVAILABLE quando o servidor não está dando conta. Ou seja, nunca tive de usar eles a nível de aplicação.
Não espero esgotar este assunto com apenas este artigo, mas ao menos dar uma ajuda e ideia geral de uso de alguns status codes populares para quem ainda está começando esta jornada com RESTful.