Playground: Swagger no Asp.Net Core com Swashbuckle
Tags: csharp, dotnet, braziliandevs
Published: 2020-07-27 16:54:53

Olá!

Este é mais um post da sessão Playground, uma iniciativa para demonstrar, com pequenos tutoriais, tecnologias e ferramentas que entendo ter potencial para trazer ganhos aos seus projetos. 

## Apresentando o Swashbuckle

Neste post quero falar sobre o Swashbuckle, uma biblioteca muito útil para ajudar a documentar APIs via Swagger.

A principal vantagem de documentar a API com este recurso é que todos os endpoints ficam disponíveis não apenas para conhecimento como também para testes. Essa é uma grande contribuição para o processo de integração com outros times durante o desenvolvimento e também para a homologação, pois permite que a API seja validada sem a necessidade de se gerar código cliente para acioná-la.

Para tirar proveito do Swashbuckle você vai precisar de:

- Um editor ou IDE (ex.: [VSCode](https://code.visualstudio.com/download))

## Começando a aplicação

Como nossa intenção é apenas documentar uma API e expor suas formas de uso, vamos começar criando e aproveitando a Web API de exemplo do Asp.Net Core. Para isso, vamos criar a infraestrutura da aplicação:

```powershell
PS X:\code\playground-swagger> dotnet new webapi -o Playground.Swagger.WeatherForecast
```

Uma vez que vamos aproveitar a aplicação de exemplo, não precisaremos nos preocupar com a criação de um domínio com lógica de negócio como nos posts anteriores. Então, vamos à documentação!

## Instalando e configurando o Swashbuckle

Para adicionarmos o Swashbuckler ao nosso projeto, vamos entrar com os seguintes comandos no Terminal:

```powershell
PS X:\code\playground-swagger\Playground.Swagger.WeatherForecast> dotnet add package Swashbuckle.AspNetCore
```

Em seguida, vamos registrar o gerador de Swagger em nossa aplicação, para que a documentação possa ser gerada. Para isso, vamos criar uma pasta chamada `Configuration` na raíz do projeto, e adicionar o arquivo `SwaggerConfigurationExtensions.cs` com o seguinte conteúdo:

```C#
using System;
using System.IO;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace Playground.Swagger.WeatherForecast
{
    public static class SwaggerConfigurationExtensions
    {
        private const string ApiTitle = "Weather Forecast API";
        private const string ApiVersion = "v1";

        public static IServiceCollection AddCustomSwagger(this IServiceCollection services)
        {
            services.AddSwaggerGen(setup => {
                setup.SwaggerDoc(ApiVersion, new OpenApiInfo
                {
                    Version = ApiVersion,
                    Title = ApiTitle,
                    Description = "API to consume weather foreacast",
                    Contact= new OpenApiContact
                    {
                        Name = "William Santos",
                        Email = "me@wsantos.dev",
                        Url = new Uri("https://wsantos.dev")
                    },
                    License = new OpenApiLicense
                    {
                        Name = "Use under MIT License",
                        Url = new Uri("https://opensource.org/licenses/MIT"),
                    }
                });

                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                setup.IncludeXmlComments(xmlPath);
            });

            return services;
        }

        public static IApplicationBuilder UseCustomSwagger(this IApplicationBuilder app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(setup => 
            {
                setup.SwaggerEndpoint($"/swagger/{ApiVersion}/swagger.json", ApiTitle);
                setup.RoutePrefix = string.Empty;
                setup.DocumentTitle = ApiTitle;
            });

            return app;
        }
    }
}
```

O código é um pouco extenso mas não é complexo. Vamos detalhá-lo!

O primeiro método desta classe de métodos de extensão é o `AddCustomSwagger`. Este método registra o gerador de Swagger em nossa aplicação, e inclui algumas informações iniciais para enriquecer a documentação. Observe que, no início do arquivo, é importado o namespace  `Microsoft.OpenApi.Models`, e este namespace abriga a classe `OpenApiInfo` que é a responsável por conter os dados básicos da documentação, como o contato do desenvolvedor e o modelo de licença da API.

Ainda no registro do gerador de Swagger, temos instruções para que ele consuma um arquivo `.xml` com os comentários neste formato que adionaremos ao nosso controller à frente. 

Já no método `UseCustomSwagger` informamos à aplicação que ela deve usar o gerador de Swagger, e servir a documentação gerada em `/swagger/v1/swagger.json`, aproveitando-a para gerar a UI do Swagger, que veremos à frente.

> **Importante**: Neste exemplo listamos apenas uma versão de nossa API, mas é possível listar múltiplas. Para saber como acesse a documentação do [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle), em inglês. 

Para finalizar a configuração do Swashbuckle, vamos incluir nossos métodos de extensão no arquivo `Startup.cs`, na raiz do projeto, da seguinte forma:

```c#
public void ConfigureServices(IServiceCollection services)
{
	...
	services.AddCustomSwagger();
}
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	...
	app.UseCustomSwagger();
}
```

Pronto! Agora podemos documentar os endpoints de nossa API para exibir na UI do Swagger.

## Documentando a API

Esta é a parte mais simples do processo: vamos adicionar comentários XML em nosso controller para que sejam carregados e exibidos na UI do Swagger.

No arquivo `WeatherForecastController`, na pasta `Controllers`, vamos adicionar o seguinte conteúdo logo acima do método `Get()`:

```xml
///<summary>
/// Gets weather forecast for the next days asked.
///</summary>
///<param name="Days"></param>
///<remarks>Sample: GET 10</remarks>
///<returns>The forecast for the informed period </returns>
///<response code="200">Returns the weather forecast list </response>
```

Acima temos um resumo do que faz o método, os parâmetros que ele pode receber (neste caso apenas um, `days`), o que o método retorna, e quais status HTTP podem ser aguardados como retorno.

> **Importante**: Como estamos testando um código trivial, apenas um status 200 é suficiente. Considere incluir outros status como 400 (para tratar parâmetros inválidos), 404 (para informar que o recurso solicitado não foi encontrado) ou mesmo 500 (caso alguma exceção ocorra).

Agora vamos adequar o método `Get` para aceitar o parâmetro `days`. O método deve ser o seguinte:

```c#
[HttpGet("{days}")]
public IEnumerable<WeatherForecast> Get(int days)
{
	var rng = new Random();
	return Enumerable.Range(1, days).Select(index => new WeatherForecast
	{
		Date = DateTime.Now.AddDays(index),
		TemperatureC = rng.Next(-20, 55),
		Summary = Summaries[rng.Next(Summaries.Length)]
	})
	.ToArray();
}
```

E, por fim, vamos avisar ao Swagger que nossa API retorna seu conteúdo em formato JSON. Vamos adicionar o seguinte conteúdo ao nosso controller:

```c#
using System.Net.Mime;
...

namespace Playground.Swagger.WeatherForecast.Controllers
{
	...
	[Produces(MediaTypeNames.Application.Json)]
	public class WeatherForecastController : ControllerBase
	{
    	...
	}   
}
```

Pronto! Agora temos nosso método documentado e pronto para ser apresentado na UI do Swagger.

## Toques Finais

Por fim, antes de testarmos nossa aplicação, vamos ao arquivo do projeto `Playground.Swagger.WeatherForecast.csproj` informar à aplicação que desejamos gerar nosso arquivo de documentação baseado nos comentários XML. Assim os comentários serão lidos e salvos no arquivo `.xml` que indicamos na configuração do Swashbuckle. Ao mesmo tempo, vamos informar que não queremos receber warnings para o caso de não documentarmos algum método:

```xml
<PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
	<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
```

## E pronto! 

Temos nossa API devidamente documentada e testável.

Se tudo deu certo, o resultado deve ser o seguinte:

![](https://dev-to-uploads.s3.amazonaws.com/i/uayc4yc4ze67iy3lfkwr.png)

Para ter acesso ao código-fonte da aplicação, acesse meu [GitHub](https://github.com/wsantosdev/playground-swagger)

## Feedback

Gostou? Me deixe saber pelos indicadores. Dúvidas? Me pergunte nos comentários que respondo assim que puder!

Até a próxima!

> Referências:
> [Swashbuckle - GitHub](https://github.com/domaindrivendev/Swashbuckle)
> [Introdução ao Swashbuckle e ao ASP.NET Core](https://docs.microsoft.com/pt-br/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-3.1&tabs=visual-studio-code)