ASP.NET Core Web API - Tratamento de erros - II


 Neste artigo vamos apresentar uma abordagem de tratamento de erros que pode ser usada em aplicações ASP .NET Core Web API.

Continuando a primeira parte do artigo veremos como customizar o tratamento de erros de forma mais estruturada.

A abordagem de construir objetos anônimos em tempo real, embora funcione, não é a mais indicada se desejamos ter um projeto robusto.

Para ter um tratamento de erros mais consistente nossa WEb API deve retornar a mesma estrutura de resposta em todos os casos, incluindo os casos de requisição mal sucedidas.

Vejamos um exemplo de implementação usando esta abordagem.

Customizando o tratamento de erros

Em seu projeto WEb API crie uma pasta onde vamos implementar 3 classes para estruturar de forma mais consistente o tratamento de erros. Eu vou criar uma pasta chamada Utils.

Nesta pasta crie uma classe chamada ApiResponse ou outro nome a seu gosto:

using Newtonsoft.Json;
namespace ProdutosApi.Utils
{
    public class ApiResponse
    {
        public int StatusCode { get; }
        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public string Message { get; }
        public ApiResponse(int statusCode, string message = null)
        {
            StatusCode = statusCode;
            Message = message ?? GetDefaultMessageForStatusCode(statusCode);
        }
        private static string GetDefaultMessageForStatusCode(int statusCode)
        {
            switch (statusCode)
            {
                ...
                case 401:
                    return "Não autorizado (não autenticado)";
                case 404:
                    return "Recurso não encontrado";
                case 405:
                    return "Método não permitido";
                case 500:
                    return "Um erro não tratado ocorreu no request";
                default:
                    return null;
        }
    }
  }
}

Esta classe apresenta duas propriedades e um método :

Agora vamos definir outra classe chamada ApiOkResponse que herda desta classe e que trata as repostas com sucesso:

namespace ProdutosApi.Utils
{
    public class ApiOkResponse : ApiResponse
    {
        public object Result { get; }
        public ApiOkResponse(object result)
            : base(200)
        {
            Result = result;
        }
    }
}

Essa classe trata das mensagens para status code 200 que indica uma requisição bem sucedida.

Agora para usar basta definir a chamada no controlador conforme o erro esperado.

Como exemplo para o método HttpDelete onde passamos um id para deletar o produto podemos fazer assim:

        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduto(int id)
        {
            var produto = await _ctx.Produtos.FindAsync(id);
            if (produto == null)
            {
                return NotFound(new ApiResponse(404, $"Produto não encontrado.( id ={id}) "));
            }
            _ctx.Produtos.Remove(produto);
            await _ctx.SaveChangesAsync();
            return Ok(new ApiOkResponse(produto));
        }

 

Fazendo uma requisição POST usando o Postman, para deletar um produto id inexistente temos o resultado abaixo:

 

Agora testando para excluir um produto existente temos o resultado abaixo:

Bem mais estruturado !!!

Para otimizar o código em nosso projeto podemos criar um filtro Action para reduzir o código usado nas Actions e evitar duplicar código.

Nota: Para detalhes em como usar os Filters veja o meu artigo aqui.

Crie uma classe chamada ApiValidationFilterAttribute  na pasta Utils que herda de ActionFilterAttribute e define o código abaixo:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace ProdutosApi.Utils
{
    public class ApiValidationFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new BadRequestObjectResult(new ApiBadRequestResponse(context.ModelState)));
            }
            base.OnActionExecuting(context);
        }
    }
}

Precisamos agora criar a classe ApiBadRequestResponse na pasta Utils com o código abaixo:

using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ProdutosApi.Utils
{
    public class ApiBadRequestResponse : ApiResponse
    {
        public IEnumerable<string> Errors { get; }
        public ApiBadRequestResponse(ModelStateDictionary modelState)
            : base(400)
        {
            if (modelState.IsValid)
            {
                throw new ArgumentException("ModelState deve ser inválido", nameof(modelState));
            }
            Errors = modelState.SelectMany(x => x.Value.Errors)
                .Select(x => x.ErrorMessage).ToArray();
        }
    }
}

Temos agora um tratamento de erro mais eficiente e centralizado.

Existem outras abordagens para este tema mas vamos tratar disso em outro artigo.

"Qual é mais fácil? Dizer ao paralítico: Estão perdoados os teus pecados, ou dizer: Levanta-te, toma o teu leito e anda?
Ora, para que saibais que o Filho do Homem tem sobre a terra autoridade para perdoar pecados — disse ao paralítico:  Eu te mando: Levanta-te, toma o teu leito e vai para tua casa."

Marcos 2:9-11

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

 

 Referências:


José Carlos Macoratti