Práticas recomendadas para tratamento de erros da API REST
1. Introdução
REST é uma arquitetura sem estado em que os clientes podem acessar e manipular recursos em um servidor. Geralmente, os serviços REST utilizam HTTP para anunciar um conjunto de recursos que eles gerenciam e fornecem uma API que permite que os clientes obtenham ou alterem o estado desses recursos.
Neste tutorial, aprenderemos sobre algumas das melhores práticas para lidar com erros de API REST, incluindo abordagens úteis para fornecer aos usuários informações relevantes, exemplos de sites de grande escala e uma implementação concreta usando um exemplo de aplicativo Spring REST.
2. Códigos de status HTTP
Quando um cliente faz uma solicitação a um servidor HTTP - e o servidor recebe a solicitação -the server must notify the client if the request was successfully handled or not. O HTTP realiza isso com cinco categorias de códigos de status:
-
Nível 100 (Informativo) - O servidor reconhece uma solicitação
-
Nível 200 (Sucesso) - O servidor concluiu a solicitação conforme o esperado
-
Nível 300 (Redirecionamento) - O cliente precisa executar ações adicionais para concluir a solicitação
-
Nível 400 (erro do cliente) - o cliente enviou uma solicitação inválida
-
Nível 500 (erro do servidor) - o servidor não conseguiu atender a uma solicitação válida devido a um erro no servidor
Com base no código de resposta, um cliente pode supor o resultado de uma solicitação específica.
3. Tratamento de erros
A primeira etapa no tratamento de erros é fornecer ao cliente um código de status adequado. Além disso, podemos precisar fornecer mais informações no corpo da resposta.
3.1. Respostas básicas
A maneira mais simples de lidar com erros érespond with an appropriate status code.
Alguns códigos de resposta comuns incluem:
-
400 Solicitação inválida - Client enviou uma solicitação inválida - como falta de corpo ou parâmetro da solicitação necessária
-
401 Unauthorized - Client falhou ao autenticar com o servidor
-
403 Proibido - Cliente autenticado, mas não tem permissão para acessar o recurso solicitado
-
404 não encontrado - o recurso solicitado não existe
-
412 Falha na pré-condição - uma ou mais condições nos campos do cabeçalho da solicitação avaliadas como falsas
-
500 Internal Server Error - Ocorreu um erro genérico no servidor
-
503 Serviço indisponível - O serviço solicitado não está disponível
Geralmente,we should not expose 500 errors to clients. 500 erros sinalizam que alguns problemas ocorreram no servidor, como uma exceção inesperada em nosso serviço REST ao manipular uma solicitação. Portanto, este erro interno não é da conta do nosso cliente.
Em vez disso,we should diligently attempt to handle or catch internal errors and respond with some 400 level response. Por exemplo, se ocorrer uma exceção porque um recurso solicitado não existe, devemos expor isso como um erro 404, não um erro 500.
Embora básicos, esses códigos permitem que o cliente entenda a natureza ampla do erro que ocorreu. Por exemplo, sabemos que, se recebermos um erro 403, não temos permissões para acessar o recurso solicitado.
Em muitos casos, porém, precisamos fornecer detalhes adicionais em nossas respostas.
3.2. Respostas de erro padrão da primavera
Esses princípios são tão onipresentes que o Spring os codificou em seuerror handling mechanism padrão.
Para demonstrar, suponha que temosa simple Spring REST application que gerencia livros, com um endpoint para recuperar um livro por seu ID:
curl -X GET -H "Accept: application/json" http://localhost:8080/api/book/1
Se não houver um livro com ID 1, esperamos que nosso controlador lance umBookNotFoundException. Ao executar um GET nesse terminal, vemos que essa exceção foi lançada e o corpo da resposta é:
{
"timestamp":"2019-09-16T22:14:45.624+0000",
"status":500,
"error":"Internal Server Error",
"message":"No message available",
"path":"/api/book/1"
}
Observe que este manipulador de erro padrão inclui um carimbo de data / hora de quando o erro ocorreu, o código de status HTTP, um título (o campoerror), uma mensagem (que está em branco por padrão) e o caminho do URL onde o erro ocorreu .
These fields provide a client or developer with information to help troubleshoot the probleme também constituem alguns dos campos que compõem os mecanismos de tratamento de erros padrão.
Além disso, observe que o Spring retorna automaticamente um código de status HTTP de 500 quando nossoBookNotFoundException é lançado. Embora algumas APIs retornem um código de status 500 - ou código de status 400, como veremos com as APIs do Facebook e Twitter - para todos os erros por questão de simplicidade,it is best to use the most specific error code when possible.
3.3. Respostas mais detalhadas
Como visto no exemplo acima do Spring, às vezes um código de status não é suficiente para mostrar as especificidades do erro. Quando necessário, podemos usar o corpo da resposta para fornecer ao cliente informações adicionais. Ao fornecer respostas detalhadas, devemos incluir:
-
Erro - um identificador exclusivo para o erro
-
Mensagem - Uma breve mensagem legível por humanos
-
Detalhe - Uma explicação mais longa do erro
Por exemplo, se um cliente envia uma solicitação com credenciais incorretas, podemos enviar uma resposta 403 com um corpo de:
{
"error": "auth-0001",
"message": "Incorrect username and password",
"detail": "Ensure that the username and password included in the request are correct"
}
The error field should not match the response code. Em vez disso, deve ser um código de erro exclusivo para nosso aplicativo. Geralmente, não há convenção para o campoerror, espere que seja exclusivo.
Normalmente, esse campo contém apenas caracteres alfanuméricos e de conexão, como traços ou sublinhados. Por exemplo,0001,auth-0001 eincorrect-user-pass são exemplos canônicos de códigos de erro.
The message portion of the body is usually considered presentable on user interfaces. Portanto, devemos traduzir este título se apoiarmosinternationalization. Portanto, se um cliente enviar uma solicitação com um cabeçalhoAccept-Language correspondente ao francês, o valortitle deve ser traduzido para o francês.
The detail portion is intended for use by developers of clients and not the end-user, portanto a tradução não é necessária.
Além disso, também podemos fornecer um URL - como o campohelp - que os clientes podem seguir para descobrir mais informações:
{
"error": "auth-0001",
"message": "Incorrect username and password",
"detail": "Ensure that the username and password included in the request are correct",
"help": "https://example.com/help/error/auth-0001"
}
Às vezes,we may want to report more than one error for a request. Nesse caso, devemos retornar os erros em uma lista:
{
"errors": [
{
"error": "auth-0001",
"message": "Incorrect username and password",
"detail": "Ensure that the username and password included in the request are correct",
"help": "https://example.com/help/error/auth-0001"
},
...
]
}
E quando ocorre um único erro, respondemos com uma lista contendo um elemento. Observe que responder com vários erros pode ser muito complicado para aplicativos simples. Em muitos casos, responder com o primeiro ou mais erro significativo é suficiente.
3.4. Organismos de resposta padronizados
Enquanto a maioria das APIs REST segue convenções semelhantes, as especificidades geralmente variam, incluindo os nomes dos campos e as informações incluídas no corpo da resposta. Essas diferenças dificultam que bibliotecas e estruturas tratem os erros de maneira uniforme.
Em um esforço para padronizar o tratamento de erros da API REST,the IETF devised RFC 7807, which creates a generalized error-handling schema.
Este esquema é composto por cinco partes:
-
type - um identificador de URI que categoriza o erro
-
title - Uma mensagem breve e legível sobre o erro
-
status - o código de resposta HTTP (opcional)
-
detail - Uma explicação legível do erro
-
instance - Um URI que identifica a ocorrência específica do erro
Em vez de usar nosso corpo de resposta de erro personalizado, podemos converter nosso corpo para:
{
"type": "/errors/incorrect-user-pass",
"title": "Incorrect username or password.",
"status": 403,
"detail": "Authentication failed due to incorrect username or password.",
"instance": "/login/log/abc123"
}
Observe que o campotype categoriza o tipo de erro, enquantoinstance identifica uma ocorrência específica do erro de maneira semelhante a classes e objetos, respectivamente.
By using URIs, clients can follow these paths to find more information about the error da mesma maneira queHATEOAS links pode ser usado para navegar em uma API REST.
A adesão ao RFC 7807 é opcional, mas é vantajoso se a uniformidade for desejada.
4. Exemplos
As práticas acima são comuns em algumas das APIs REST mais populares. Embora os nomes específicos dos campos ou formatos possam variar entre os sites,the general patterns are nearly universal.
4.1. Twitter
Por exemplo, vamos enviar uma solicitação GET sem fornecer os dados de autenticação necessários:
curl -X GET https://api.twitter.com/1.1/statuses/update.json?include_entities=true
A API do Twitter responde com um erro 400 no seguinte corpo:
{
"errors": [
{
"code":215,
"message":"Bad Authentication data."
}
]
}
Esta resposta inclui uma lista contendo um único erro, com seu código e mensagem de erro. No caso do Twitter, nenhuma mensagem detalhada está presente e um erro 400 geral - em vez de um erro 401 mais específico - é usado para denotar que a autenticação falhou.
Sometimes a more general status code is easier to implement, como veremos em nosso exemplo Spring abaixo. Ele permite que os desenvolvedores capturem grupos de exceções e não diferenciam o código de status que deve ser retornado. Quando possível, porém,the most specific status code should be used.
4.2. Facebook
Semelhante ao Twitter,Facebook’s Graph REST API também inclui informações detalhadas em suas respostas.
Por exemplo, vamos realizar uma solicitação POST para autenticar com a API Graph do Facebook:
curl -X GET https://graph.facebook.com/oauth/access_token?client_id=foo&client_secret=bar&grant_type=baz
Recebemos o seguinte erro:
{
"error": {
"message": "Missing redirect_uri parameter.",
"type": "OAuthException",
"code": 191,
"fbtrace_id": "AWswcVwbcqfgrSgjG80MtqJ"
}
}
Como o Twitter, o Facebook também usa um erro genérico de 400 - em vez de um erro mais específico de nível 400 - para denotar uma falha. Além de uma mensagem e um código numérico, o Facebook também inclui um campotype que categoriza o erro e um ID de rastreamento (fbtrace_id) que atua como uminternal support identifier.
5. Conclusão
Neste artigo, examinamos algumas das práticas recomendadas para o tratamento de erros da API REST, incluindo:
-
Fornecendo códigos de status específicos
-
Incluindo informações adicionais nos corpos de resposta
-
Manipulando exceções de maneira uniforme
Embora os detalhes do tratamento de erros variem de acordo com o aplicativo,these general principles apply to nearly all REST APIs and should be adhered to when possible.
Isso não apenas permite aos clientes manipular erros de maneira consistente, mas também simplifica o código que criamos ao implementar uma API REST.
O código referenciado neste artigo está disponívelover on GitHub.