Dev (Back & Front)ARTIGO

ASP.NET Core 2.2: Preview 2, primeiros passos e API Conventions

Renato Groffe dá início a sua série de artigos sobre as novidades do ASP.NET Core 2.2 apresentando os primeiros passos no framework falando sobre a API Conventions.

Durante o mês de setembro/2018, foi disponibilizado o Preview 2 do ASP.NET Core 2.2, com o anúncio oficial acontecendo juntamente com as versões 2.2 do .NET Core e do Entity Framework Core durante o .NET Conf 2018.

Informações sobre estes releases podem ser encontradas nos seguintes artigos:

Este primeiro artigo marca o início de uma série que produzirei sobre as novidades do ASP.NET Core 2.2. Além de orientações para a instalação do Preview 2, abordarei ainda o uso de API Conventions como meio para facilitar a documentação de APIs REST em conjunto com o Swagger.

Instalando o Preview 2 do .NET Core 2.2 e do Visual Studio 2017 15.9

Para iniciar a utilização do ASP.NET Core 2.2, será necessário proceder com a instalação do SDK do Preview 2 do .NET Core 2.2. Compatível com Windows, diversas distribuições Linux e macOS, o instalador do .NET Core 2.2 está disponível através do seguinte link:

A imagem a seguir traz o resultado da execução do comando dotnet –version, com a mesma indicando que o Preview 2 foi implantado com sucesso:

Atualmente o Preview 2 do release 15.9.0 do Visual Studio 2017 possui suporte ao ASP.NET Core 2.2. Esta versão pode ser obtida a partir do próprio instalador da IDE:

Caso todo este processo tenha ocorrido sem maiores dificuldades, no Visual Studio será possível observar como opção os templates para a versão 2.2 do ASP.NET Core:

API Conventions

Uma prática bastante comum ao se empregar o Swagger na documentação de APIs REST com o ASP.NET Core está no uso do atributo ProducesResponseType, o qual permite indicar os prováveis retornos de uma Action. Na listagem a seguir temos um Controller (classe CatalogoContext) que exemplifica esta técnica:

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Swagger;

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

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

        [HttpGet("produtos/{codigo}")]
        [ProducesResponseType((int)HttpStatusCode.NotFound)]
        [ProducesResponseType(typeof(Produto), (int)HttpStatusCode.OK)]
        public ActionResult<Produto> GetProduto(string codigo)
        {
            Produto prod = null;
            if (codigo.StartsWith("PROD"))
                prod = _contexto.ObterItem<Produto>(codigo);

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

        [HttpGet("servicos/{codigo}")]
        [ProducesResponseType((int)HttpStatusCode.NotFound)]
        [ProducesResponseType(typeof(Servico), (int)HttpStatusCode.OK)]
        public ActionResult<Servico> GetServico(string codigo)
        {
            Servico serv = null;
            if (codigo.StartsWith("SERV"))
                serv = _contexto.ObterItem<Servico>(codigo);

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

O ASP.NET Core 2.2 conta agora com as API Conventions. Trata-se, na prática, de um conjunto de convenções que podem ser empregadas ao invés do atributo ProducesResponseType, simplificando assim o processo de documentação de uma API e contribuindo ainda para um código mais enxuto.

A próxima listagem traz este primeiro exemplo modificado, de forma a utilizar atributo ApiConventionMethod nas diferentes Actions do Controller:

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Swagger;

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

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

        [HttpGet("produtos/{codigo}")]
        [ApiConventionMethod(typeof(DefaultApiConventions), nameof(DefaultApiConventions.Get))]
        public ActionResult<Produto> GetProduto(string codigo)
        {
            Produto prod = null;
            if (codigo.StartsWith("PROD"))
                prod = _contexto.ObterItem<Produto>(codigo);

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

        [HttpGet("servicos/{codigo}")]
        [ApiConventionMethod(typeof(DefaultApiConventions), nameof(DefaultApiConventions.Get))]
        public ActionResult<Servico> GetServico(string codigo)
        {
            Servico serv = null;
            if (codigo.StartsWith("SERV"))
                serv = _contexto.ObterItem<Servico>(codigo);

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

Ao analisar o resultado gerado pelo Swagger, será possível observar como retornos possíveis os códigos HTTP 200 (OK) e 404 (Not Found):

Este mesmo tipo de comportamento pode ser aplicado a um Controller através do atributo ApiConventionType, o qual receberá como parâmetro o tipo DefaultApiConventions:

using Microsoft.AspNetCore.Mvc;

namespace APICatalogo.Controllers
{
    [ApiConventionType(typeof(DefaultApiConventions))]
    [Route("api/[controller]")]
    [ApiController]
    public class ProdutosController : ControllerBase
    {
        private CatalogoContext _contexto;

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

        [HttpGet("{id}")]
        public ActionResult<Produto> Get(string id)
        {
            Produto prod = null;
            if (id.StartsWith("PROD"))
                prod = _contexto.ObterItem<Produto>(id);

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

Ou ainda a todo um projeto combinando a utilização de ApiConventionType e DefaultApiConventions com o atributo assembly:

using Microsoft.AspNetCore.Mvc;

[assembly: ApiConventionType(typeof(DefaultApiConventions))]
namespace APICatalogo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProdutosController : ControllerBase
    {
        private CatalogoContext _contexto;

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

        [HttpGet("{id}")]
        public ActionResult<Produto> Get(string id)
        {
            Produto prod = null;
            if (id.StartsWith("PROD"))
                prod = _contexto.ObterItem<Produto>(id);

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

Referências

Atua como consultor em atividades voltadas ao desenvolvimento de sistemas há mais de 15 anos. Microsoft MVP (Most Valuable Professional) e participante do programa MTAC (Multi-Plataform Technical Audience Contributor). Bacharel em Sistemas de Informação, com Especialização em Engenharia de Software e MBA em Business Intelligence. Também é palestrante e autor técnico em portais e revistas especializadas, com foco em tecnologias Microsoft e boas práticas na área de software.

Ver perfil