ASP.NET Core - Apresentando a classe ProblemDetails
Hoje vou apresentar a classe ProblemDetails da ASP.NET Core e mostrar como usá-la na ASP .NET Core. |
Em uma Web API para comunicar os erros e exceções aos clientes devemos especificar um formato de resposta. E muitas vezes desejamos informar detalhes da ocorrência ao invés de apenas dizer que ocorreu um erro 400 ou 500.
Como existem milhares de APIs e cada uma define um formato de resposta diferente acaba que não temos um formato padronizado e isso podemos constatar verificando APIs muito consumidas como o Facebook e o Twitter.
Para resolver este problema foi criada em 2016 um documento chamado Problem Details for HTTP APIs definido pela IETF (Internet Enginnering Task Force), instituição que especifica os padrões que serão implementados e utilizados em toda a internet., na RFC 7807 que cria um formato padronizado para os formatos de mensagens de erros em APIs HTTP e define o objeto Problem Detail. O objetivo é evitar que novos formatos sejam criados e ela especifica o seguinte:
application/problem+json application/problem+xml |
Title -> um breve resumo do tipo de problema. Não deve mudar para ocorrências do mesmo tipo, exceto para fins de localização; Detail -> Uma descrição detalhada do problema; Type -> Uma URL para um documento que descreva o tipo do problema; Status -> O status HTTP gerado pelo servidor de origem. Normalmente deve ser o mesmo status HTTP da resposta, e pode servir de referência para casos onde um servidor proxy altera o status da resposta; Instance -> Propriedade opcional, com um URI exclusivo para o erro específico, que geralmente aponta para um log de erros para essa resposta. |
Vejamos um exemplo de uma resposta formatada usando o objeto ProblemDetails:
Aqui estamos tentando acessar um produto que não existe mais, e, como podemos ver nos cabeçalhos de resposta, o objeto JSON Problem Details é do tipo “appalication/problem+json”, conforme especifica a documentação, e, ele contém os seguintes membros:
O membro “Type”
é considerado vazio quando não for especificado. Quando existe, ele fornece
uma referência legível que descreve o tipo de problema em geral. Aqui, na
resposta de exemplo, isso leva a uma página de erro personalizada. Essa página
explicaria ao usuário que o produto que ele estava procurando não existe. Em
outros casos, pode, por exemplo, vincular-se à descrição do código de status
HTTP.
O membro “Status” é apenas consultivo. Pode ser
útil em alguns casos em que queremos confirmar que nosso cabeçalho de resposta
não foi adulterado. Deve sempre corresponder ao código de status gerado pelo
servidor especificado nos cabeçalhos de resposta. Isso é para garantir que o
cliente se comporte corretamente, mesmo que não entenda o formato
ProblemDetails.
O membro “Instance” identifica a ocorrência
específica do problema e pode ou não fazer referência a informações mais
detalhadas.
Além do formato padrão especificado do objeto Problem
Details, podemos estendê-lo se quisermos adicionar informações
personalizadas. É importante notar que este objeto não se destina a ser
uma ferramenta de depuração. Devemos sempre ter cuidado ao expor nossos detalhes
de implementação nessas respostas.
A implementação ASP .NET Core
Desde a versão 2.1 do .NET Core, o objeto Problem Details é representado pela classe ProblemDetails que é responsável por definir um formato legível para especificar erros nas respostas das APIs HTTP com base na RFC 7807.
Assim, a partir da versão versão 2.2 da ASP .NET Core, quando usamos os métodos internos da classe ControllerBase para retornar as respostas do código de status HTTP, como Ok() ou BadRequest(), o framework formata automaticamente a resposta como base na classe ProblemDetails seguindo assim o que recomenda a RFC 7807.
Então na ASP.NET Core, podemos usar o recurso da classe ProblemDetails nos controladores simplesmente chamando o método Problem() de ControllerBase que retorna um IActionResult conforme mostra o exemplo a seguir:
[HttpGet("{id}", Name = "ObterCategoria")] public ActionResult<Categoria> Get(int id) { var categoria = _context.Categorias.AsNoTracking() .FirstOrDefault(p => p.CategoriaId == id);
if (categoria == null) return categoria; |
O resultado obtido no navegador ao tentar localizar uma categoria inexistente pode ser visto a abaixo:
Como nada é perfeito, o objeto Problem Details não tem uma maneira explícita de definir vários problemas em uma única resposta. Você pode conseguir isso definindo um tipo específico, o que indica que haverá um membro de problemas que será uma matriz dos membros de detalhes de problemas normais.
Para realizar o tratamento de exceções o framework não nos dá muitas opções e temos que fazer todo o trabalho via código. Como alternativa você pode usar o pacote Hellang.Middleware.ProblemDetails de Kristian Hellang, que é um middleware que mapeia exceções para ProblemDetais e que veremos na próxima parte do artigo.
E estamos conversados...
"Aguardo
ansiosamente e espero que em nada serei envergonhado. Pelo contrário, com toda a
determinação de sempre, também agora Cristo será engrandecido em meu corpo, quer
pela vida quer pela morte;
porque para mim o viver é Cristo e o morrer é lucro "
Filipenses 1:20-21
Referências:
C# - Lendo e escrevendo em arquivos textos e binários
C#- Apresentando Streams assíncronos
ASP .NET - Apresentando Web API - Macoratti.net
Três maneiras de retornar dados de uma Web API
ASP.NET Core Web API - Tratamento de erros
ASP.NET Core Web API - Tratando erros globalmente