ASP.NET Core Web API - Apresentando API Analyzers


Neste artigo vamos apresentar o recurso API Analyzers introduzido na versão 2.2 da ASP .NET Core.

Na ASP .NET Core 2.1 foi introduzido o atributo [ApiController] para denotar convenções específicas de um Controller Web API e realizar a validação do modelo com resposta automática de erro 400.

A ASP .NET Core 2.2 vai além fornecendo os metadados para a API Explorer, melhorando assim a experiência de documentação da API. Para fazer isso a versão 2.2 introduziu o recurso API Analyzers que ajudam a seguir um conjunto de convenções para uma melhor documentação da API.

Nesta artigo vamos descobrir como usar os  API Analyzers ou Analisadores de API.

Criando o projeto API no VS 2019 Community

Vamos criar uma aplicação ASP .NET Core Web Application usando o template API no Visual Studio 2019 Community chamada Api_AnalyzersDemo;

Abra o VS 2019 Community e selecione : Create a new Project

Selecionando em Language : C#, Platform: All Platforms e em Project Type: web podemos selecionar o template ASP .NET Core Web Application:

A seguir vamos informar o nome do projeto, sua localização e nome da solução conforme abaixo:

Finalmente seleciona a plataforma .NET Core e ASP .NET Core 2.2 e o template API.

Marque a opção para configurar HTTP e não vamos usar Autenticação:

Ao final teremos um projeto Web API com a estrutura exibida na janela Solution Explorer:

Incluinndo o Swagger para documentar a API

Depois que o projeto for criado, precisaremos ativar a documentação do Swagger para a nossa API.

Vou mostrar bem resumidamente como fazer isso. Para detalhes veja o meu vídeo: Gerando a documentação com Swagger

Inclua uma referência no projeto ao pacote Swashbuckle.AspNetCore:

A seguir no método ConfigureServices da classe Startup adiciona o serviço Swagger ao middleware:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info
                {
                    Version = "v1",
                    Title = "Api Analyzers",
                    Description = "API Demonstração - API Analyzers",
                    TermsOfService = "Nenhum",
                    Contact = new Contact() { Name = "Macoratti.net", Email = "macoratti@yahoo.com", Url = "www.macoratti.net" }
                });
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

A seguir vamos habilitar a UI Swagger no método Configure() :

       public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseMvc();

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Mac API V1");
            });
        }

Pronto ! Isso é tudo que precisamos para configurar o Swagger para documentar a API.

Criando um novo Controlador

Para mostrar a utilidade dos API Analyzers ou Analisadores de API, precisamos de um exemplo de código real (basicamente, um controlador com operações CRUD).

Vamos definir um modelo de domínio, criar um novo Controller e definir as operações CRUD no controlador.

Primeiro vamos remover o controlador ValuesController da pasta Controllers.

A seguir crie uma pasta Models no projeto e nesta pasta crie um

    public class Aluno
    {
        public int AlunoId { get; set; }
        public string Nome { get; set; }
        public int Idade { get; set; }
    }

Vamos fazer a conexão com a tabela Alunos do banco de dados EscolaDB no SQL Server usando o EF Core. Abaixo vemos os dados existentes na tabela Alunos:

Para isso vamos criar a classe de contexto AppDbContext na pasta Models para mapear a entidade Aluno para a tabela Alunos existente.

Crie a classe AppDbContext na pasta Models com o código abaixo:

using Microsoft.EntityFrameworkCore;
namespace Api_AnalyzersDemo.Models
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {}

        public DbSet<Aluno> Alunos{ get; set; }
    }
}

Note que esta classe herda de DbContext e define um DbSet<Aluno> mapeando para a tabela Alunos.

Agora precisamos registrar o contexto como um serviço e definir o provedor do banco de dados e a string de conexão no método ConfigureServices da classe Startup incluindo a linha de código abaixo:

...
 services.AddDbContext<AppDbContext>(options => 
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
...

Abra o arquivo appsettings.json e defina a string de conexão DefaultConnection :

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=Macoratti;Initial Catalog=EscolaDB;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Agora temos tudo configurado e podemos criar o controlador.

Criando o controlador AlunosController

Vamos agora criar o controlador AlunosController usando o Scaffold e definir os métodos CRUD com a integração do EF Core.

Clique com o botão direito do mouse sobre a pasta Controllers e selecione a opção Add Controller;

Na janela Add Scaffold escolha a opção API Controller with actions using Entity Framework;

Na janela a seguir informe os dados conforme mostrado na figura abaixo:

Após clicar no botão Add, nosso controlador AlunosController será criado na pasta Controllers, e, já poderemos testar nossa aplicação.

Executando o projeto e navegando para a url : https://localhost:44326/swagger/

Iremos acessar a documentação da nossa API gerada pelo Swagger conforme mostra a figura abaixo:

Clique no botão PUT para visualizar detalhes do método PUT e sua implementação:

Observe que na documentação do Swagger nossa API PUT esta retornando apenas o status code 200, mas se espiarmos o código do método PutAluno no controlador AlunosController veremos o seguinte:

...
       // PUT: api/Alunos/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutAluno(int id, Aluno aluno)
        {
            if (id != aluno.AlunoId)
            {
                return BadRequest();
            }
            _context.Entry(aluno).State = EntityState.Modified;
            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!AlunoExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return NoContent();
        }
...

Note que no código temos 3 possíveis retornos:

Assim nossa documentação esta omissa quanto a esses tipos de retornos esperados no método Http PUT.

Aqui entra o recurso API Analyzers, e, vamos ver como usá-lo para corrigir esta falha.

Instalando e usando API Analyzers

Por padrão, a ASP.NET Core 2.2 é fornecida com um conjunto de convenções padrão - DefaultApiConventions - baseadas no controlador que o Scaffold da ASP.NET Core gera.

Se seus métodos Actions seguem o padrão que o scaffolding produz, você deve ser bem sucedido usando as convenções padrão. Estes analisadores trabalham com controladores anotados com o atributo ApiController introduzido na versão 2.1.

Para usar os analisadores de API, temos que instalar o pacote nuget : Microsoft.AspNetCore.MVC.Api.Analyzers

Abra a janela - Package Manager Console - e digite comando : Install-package <nome_pacote>

Após a instalação do pacote nuget, se você retornar aos métodos Action do controlador AlunosController e observar o método PutAluno vai notar que existem alertas (warnings) nas 3 linhas de return do código conforme mostra a figura abaixo:

O analizador reporta a falta da documentação para os códigos de status e oferece uma opção para corrigir o problema.

Clicando em um dos alertas e selecionando a ferramenta para fazer a correção veremos a sugestão que vai incluir os atributos ProducesResponseType(StatusCode.<código_status_cod> no método PutAluno, conforme mostra a figura abaixo:

Aceitando a sugestão para correção teremos o resultado mostrado a seguir:

Com isso a geração da documentação vai incluir esses códigos de status de response conforme vemos na figura abaixo para o método HTTP Put:

Aplicando as convenções da Web API

O ASP.NET Core 2.2 e versões posteriores incluem uma maneira de extrair documentação comum da API e aplicá-la a várias Actions, controladores ou todos os controladores dentro de um assembly.

As convenções da Web API são um substituto para decorar ações individuais com [ProducesResponseType].

Uma convenção permite que você:

As Convenções não compõem; cada Action pode estar associada a exatamente uma convenção. Convenções mais específicas têm precedência sobre convenções menos específicas. A seleção  não é determinística quando duas ou mais convenções da mesma prioridade se aplicam a uma ação.

As seguintes opções existem para aplicar uma convenção a uma Action, do mais específico ao menos específico:

1- Microsoft.AspNetCore.Mvc.ApiConventionMethodAttribute

Aplicada às Actions individuais e especifica o tipo de convenção e o método de convenção a que se aplica.

No exemplo, o tipo de convenção padrão do método Microsoft.AspNetCore.Mvc.DefaultApiConventions.Put é aplicado para Action Update:
 
[HttpPut("{id}")]
[ApiConventionMethod(typeof(DefaultApiConventions), 
                     nameof(DefaultApiConventions.Put))]
public IActionResult Update(string id, Contato contato)
{
    var contatoToUpdate = _contatos.Get(id);
    if (contatoToUpdate == null)
    {
        return NotFound();
    }
    _contatos.Update(contato);
    return NoContent();
}

Neste exemplo a convenção Microsoft.AspNetCore.Mvc.DefaultApiConventions.Put aplica os seguintes atributos ao método Action:

[ProducesDefaultResponseType]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]

2- Microsoft.AspNetCore.Mvc.ApiConventionTypeAttribute aplicado ao Controller

Aplica o tipo de convenção especificado a todas as Actions no controlador. Um método de convenção é decorado com dicas que determinam as ações às quais o método de convenção se aplica.

No exemplo a seguir um conjunto de convenções é aplicado ao Controller Contatos:

[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
[Route("api/[controller]")]
public class ContatosController : ControllerBase
{
}

3- Microsoft.AspNetCore.Mvc.ApiConventionTypeAttribute aplicado ao  assembly

Aplica o tipo de convenção especificado a todos os controladores no assembly atual. Como uma recomendação, aplique atributos de nível de assembly no arquivo Startup.cs.

No exemplo a seguir o conjunto padrão de convenções é aplicado a todos os controladores no assembly:

assembly: ApiConventionType(typeof(DefaultApiConventions))]
namespace ApiConventions
{
    public class Startup
    {}
...
}

Nota: A classe DefaultApiConventions é uma classe estática, que define um conjunto de métodos como um padrão e os metadados aplicáveis são aplicados ao método, se o padrão for correspondido.

Pegue o projeto completo aqui:  Api_AnalyzersDemo.zip

"Dando graças ao Pai que nos fez idôneos para participar da herança dos santos na luz;
O qual nos tirou da potestade das trevas, e nos transportou para o reino do Filho do seu amor;
Em quem temos a redenção pelo seu sangue, a saber, a remissão dos pecados;
O qual é imagem do Deus invisível, o primogênito de toda a criação;"
Colossenses 1:12-15

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