.NET

18 dez, 2017

ASP.NET Core 2.0: retornando mensagens de erro em APIs REST

Publicidade

Embora muitas vezes ignorada, a geração de informações indicando a ocorrência de falhas ou estados inválidos constitui prática de fundamental importância em APIs REST bem estruturadas. O cuidado em reportar estas situações de forma clara, facilitará o trabalho em aplicações que dependam de uma API, contribuindo para que tais projetos se comportem de maneira consistente diante de circunstâncias anormais.

Este artigo traz alguns exemplos de como retornar mensagens relatando problemas em APIs baseadas no ASP.NET Core 2.0.

Exemplos de implementação

O projeto tomado como base por este artigo está no GitHub e faz uso do MongoDB. A API em questão retornará itens que compõem um catálogo de produtos e serviços:

A implementação desta aplicação foi demonstrada inclusive durante um Live Demo do Canal .NET:

No Controller a seguir, foram definidas as Actions GetProduto e GetServico:

  • Analisando a estrutura destes métodos, é possível notar que o retorno dos mesmos é uma instância baseada na interface IActionResult (namespace Microsoft.AspNetCore.Mvc);
  • Ao se informar um código válido e um item for encontrado, o retorno destas operações será uma instância do tipo ObjectResult (namespace Microsoft.AspNetCore.Mvc) contendo um produto ou serviço;
  • Ao se tratar de um código inválido ou que corresponda a um item inexistente, será então invocado o método NotFound (namespace Microsoft.AspNetCore.Mvc). Esta ação resultará na geração de um erro do tipo 404 (indicativo de um recurso não encontrado).
using Microsoft.AspNetCore.Mvc;

namespace APICatalogo.Controllers
{
    [Route("api/[controller]")]
    public class CatalogoController : Controller
    {
        private CatalogoContext _contexto;

        public CatalogoController(CatalogoContext context)
        {
            _contexto = context;
        }

        [HttpGet("produtos/{codigo}")]
        public IActionResult GetProduto(string codigo)
        {
            Produto prod = null;
            if (codigo.StartsWith("PROD"))
                prod = _contexto.ObterItem<Produto>(codigo);

            if (prod != null)
                return new ObjectResult(prod);
            else
                return NotFound();
        }

        [HttpGet("servicos/{codigo}")]
        public IActionResult GetServico(string codigo)
        {
            Servico serv = null;
            if (codigo.StartsWith("SERV"))
                serv = _contexto.ObterItem<Servico>(codigo);

            if (serv != null)
                return new ObjectResult(serv);
            else
                return NotFound();
        }
    }
}

Ao efetuar um teste via Postman com um código de produto inválido, a resposta obtida mostrará a ocorrência de um erro do tipo 404:

Já a próxima listagem traz uma versão refatorada da classe CatalogoController:

  • O método NotFound em GetProduto está recebendo como parâmetro uma string, a qual indica que o código informado é inválido ou se refere a um produto inexistente;
  • Alternativamente, uma instância poderia ser repassada como parâmetro à operação NotFound. É o que se observa no método GetServico, em que um objeto anônimo descreve um tipo de problema similar àquele de GetProduto.
using Microsoft.AspNetCore.Mvc;

namespace APICatalogo.Controllers
{
    [Route("api/[controller]")]
    public class CatalogoController : Controller
    {
        private CatalogoContext _contexto;

        public CatalogoController(CatalogoContext context)
        {
            _contexto = context;
        }

        [HttpGet("produtos/{codigo}")]
        public IActionResult GetProduto(string codigo)
        {
            Produto prod = null;
            if (codigo.StartsWith("PROD"))
                prod = _contexto.ObterItem<Produto>(codigo);

            if (prod != null)
                return new ObjectResult(prod);
            else
            {
                return NotFound(
                    "Código de produto inválido ou item inexistente.");
            }
        }

        [HttpGet("servicos/{codigo}")]
        public IActionResult GetServico(string codigo)
        {
            Servico serv = null;
            if (codigo.StartsWith("SERV"))
                serv = _contexto.ObterItem<Servico>(codigo);

            if (serv != null)
                return new ObjectResult(serv);
            else
            {
                return NotFound(
                    new
                    {
                        Mensagem = "Código de serviço inválido ou item inexistente.",
                        Erro = true
                    });
            }
        }
    }
}

O método BadRequest constitui outra opção para a geração de erros, apresentando um comportamento similar a NotFound. Ao se invocar BadRequest, será retornado um erro do tipo 400, correspondendo assim a uma requisição inválida.

Ao repetir via Postman a consulta a um código de produto inexistente, o resultado será quase idêntico ao teste inicial. A única diferença agora está na presença de mensagem de texto detalhando o problema:

 

Por fim, um teste com um código de serviço inexistente também resultará em um erro 404. A resposta produzida incluirá um JSON explicando o problema, com tal valor correspondendo ao objeto criado na Action GetServico de CatalogoController:

Referências