Após a conclusão da etapa 1, onde criamos todos os projetos e configuramos a execução do sistema, as funcionalidades básicas de nossas APIs estão todas funcionando perfeitamente.
Ao pressionar a tecla F5 para executar os projetos da nossa solução, veremos as seguintes janelas serem abertas no browser:
Embora os serviços prometidos estejam funcionando, as informações destas páginas não são nada expressivas para o consumidor de nossas APIs.
O que faremos a seguir é habilitar a documentação automática utilizando o NSwag.
Para isto, vamos executar os seguintes passos:
1. Instalando o NSwag e criando o configurador do Swagger
Acesse o menu Tools ⇒ Nuget Package Manager ⇒ Package Manager Console;
Na janela do console, no campo Default Project, selecione o projeto Erp.Shared;
Digite o comando Install-Package NSwag.AspNetCore, pressione Enter e aguarde a instalação.
Como o projeto Api.Erp.Shared está referenciado em todos os demais projetos, instalaremos o NSwag apenas neste projeto.
A seguir, criaremos dois métodos de extensão para configurar o swagger.
Crie o seguinte arquivo no diretório Extensions do projeto Api.Erp.Shared:
…Api.Erp\Api.Erp.Shared\Extensions\SwaggerConfig.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace Api.Erp.Shared.Extensions
{
public static class SwaggerConfig
{
public static void ConfigureSwaggerDoc(this IServiceCollection services, string description)
{
services.AddOpenApiDocument(config =>
{
config.Version = "v1";
config.AllowReferencesWithProperties = true; //Habilita a geração de exemplos para propriedades aninhadas
config.Title = "Documentação da API: " + Assembly.GetEntryAssembly().GetName().Name;
config.Description = description;
});
}
public static void ConfigureSwaggerUI(this IApplicationBuilder app)
{
// Ativa o middleware para veicular o Swagger gerado como um terminal JSON.
app.UseOpenApi();
// Registra o gerador Swagger e os middlewares Swagger UI
app.UseSwaggerUi3(config => config.TransformToExternalPath = (internalUiRoute, request) =>
{
config.Path = "/swagger";
if (internalUiRoute.StartsWith("/") == true && internalUiRoute.StartsWith(request.PathBase) == false)
{
return request.PathBase + internalUiRoute;
}
else
{
return internalUiRoute;
}
});
}
}
}
2. Habilitando o Swagger no projeto Api.Erp.Clientes
Acesse o arquivo Startup.cs do projeto Api.Erp.Clientes e faça as seguintes alterações:
using Api.Erp.Shared.Extensions;
public class Startup
{
…
public void ConfigureServices(IServiceCollection services)
{
…
//Habilita o Swagger
string description = "**API para gerenciamento dos clientes do sistema Api.Erp**";
services.ConfigureSwaggerDoc(description);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
…
// Ativa a Swagger-ui
app.ConfigureSwaggerUI();
…
}
}
3. Habilitando o Swagger no projeto Api.Erp.Comercial
Acesse o arquivo Startup.cs do projeto Api.Erp.Comercial e faça as seguintes alterações:
using Api.Erp.Shared.Extensions;
public class Startup
{
…
public void ConfigureServices(IServiceCollection services)
{
…
//Habilita o Swagger
string description = "**API para gerenciamento das vendas do sistema Api.Erp**";
services.ConfigureSwaggerDoc(description);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
…
// Ativa a Swagger-ui
app.ConfigureSwaggerUI();
…
}
}
4. Habilitando o Swagger no projeto Api.Erp.Fiscal
Acesse o arquivo Startup.cs do projeto Api.Erp.Fiscal e faça as seguintes alterações:
using Api.Erp.Shared.Extensions;
public class Startup
{
…
public void ConfigureServices(IServiceCollection services)
{
…
//Habilita o Swagger
string description = "**API para gerenciamento das notas fiscais do sistema Api.Erp**";
services.ConfigureSwaggerDoc(description);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
…
// Ativa a Swagger-ui
app.ConfigureSwaggerUI();
…
}
}
A partir deste momento, ao executarmos o nossas APIs (F5), caso acessemos a rota \swagger, as páginas de documentação já serão exibidas.
Nestas páginas, é possível testar todos os endpoints, clicando no botão Try it out e depois no botão Execute.
- Abrindo a rota do Swagger automaticamente
Você deve ter notado, que ao executar os projetos, ainda está sendo necessário digitar a rota que exibe a documentação do Swagger (\swagger), este processo pode ser automatizado.
Esta ação, também poderia ser feito via configuração nas propriedades do projeto. Porém, faremos de uma forma um pouco mais simples e não baseada em configuração. Siga os seguintes passos:
Crie um arquivo chamado DocsController.cs, na pasta Controller de cada um dos projetos, com os respectivos conteúdos:
…\Api.Erp\Api.Erp.Clientes\Controllers\DocsController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Api.Erp.Clientes.Controllers
{
/// <summary>
/// Controller para exibir a documentação da API de Clientes
/// </summary>
[Route("")]
[ApiExplorerSettings(IgnoreApi = true)]
public class DocsController : Controller
{
[Route(""), HttpGet]
[AllowAnonymous]
public IActionResult Swagger()
{
return Redirect("~/swagger");
}
}
}
…\Api.Erp\Api.Erp.Comercial\Controllers\DocsController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Api.Erp.Comercial.Controllers
{
/// <summary>
/// Controller para exibir a documentação da API Comercial
/// </summary>
[Route("")]
[ApiExplorerSettings(IgnoreApi = true)]
public class DocsController : Controller
{
[Route(""), HttpGet]
[AllowAnonymous]
public IActionResult Swagger()
{
return Redirect("~/swagger");
}
}
}
…\Api.Erp\Api.Erp.Fiscal\Controllers\DocsController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Api.Erp.Fiscal.Controllers
{
/// <summary>
/// Controller para exibir a documentação da API Fiscal
/// </summary>
[Route("")]
[ApiExplorerSettings(IgnoreApi = true)]
public class DocsController : Controller
{
[Route(""), HttpGet]
[AllowAnonymous]
public IActionResult Swagger()
{
return Redirect("~/swagger");
}
}
}
Agora, ao executar a solution (F5), as páginas do swagger serão carregadas automaticamente para todas as APIs, sem a necessidade de acionar a rota \swagger.
6. Melhorando os exemplos de dados gerados do Swagger
Ao examinarmos a documentação de cada endpoint, podemos verificar no campo “Example Value”, um exemplo de dados que poderiam ser submetidos para a API, porém os valor atribuídos à cada propriedade, não são sempre sugestivos para o usuário.
Veja por exemplo o JSON atribuído ao método POST da API que gerencia as notas fiscais:
Podemos melhorar estes exemplos, com a anotação JsonSchemaExtensionData da classe NJsonSchema.Annotations.JsonSchemaExtensionDataAttribute.
Vamos alterar as nossas classes, incluindo exemplos de dados mais eficientes, incluindo também a tag com um texto explicativo para o usuário (internos ou externos) de nossas APIs.
Faça as seguintes alterações nos respectivos arquivos:
…Api.Erp\Api.Erp.Shared\ViewModels\ClienteVM.cs
using NJsonSchema.Annotations;
using System;
namespace Api.Erp.Shared.ViewModels
{
/// <summary>
/// Informações para o cadastro do cliente
/// </summary>
public class ClienteVM
{
/// <summary>
/// Id do cliente no Banco de Dados
/// </summary>
[JsonSchemaExtensionData("example", "15")]
public string id { get; set; }
/// <summary>
/// Nome do cliente
/// </summary>
[JsonSchemaExtensionData("example", "Nome do cliente")]
public string nome { get; set; }
/// <summary>
/// Email do cliente
/// </summary>
[JsonSchemaExtensionData("example", "emaildocliente@provedor.com.br")]
public string email { get; set; }
}
}
…Api.Erp\Api.Erp.Shared\ViewModels\NotaFiscalVmInput.cs
using NJsonSchema.Annotations;
namespace Api.Erp.Shared.ViewModels
{
/// <summary>
/// Informações para geração de uma nova nota fiscal
/// </summary>
public class NotaFiscalVmInput
{
/// <summary>
/// Id na Nota Fiscal no Banco de Dados
/// </summary>
[JsonSchemaExtensionData("example", "31")]
public string id { get; set; }
/// <summary>
/// Id da venda que será a origem da Nota Fiscal
/// </summary>
[JsonSchemaExtensionData("example", "2")]
public string idVenda { get; set; }
/// <summary>
/// Valor total do ICMS. Formato 00000.00
/// </summary>
[JsonSchemaExtensionData("example", 1500.90)]
public decimal valorTotalICMS { get; set; }
/// <summary>
/// Valor total da nota fiscal
/// </summary>
[JsonSchemaExtensionData("example", 1500.00)]
public decimal valorTotalNotaFiscal { get; set; }
}
}
…Api.Erp\Api.Erp.Shared\ViewModels\NotaFiscalVmOutput.cs
namespace Api.Erp.Shared.ViewModels
{
/// <summary>
/// Informações da nota fiscal
/// </summary>
public class NotaFiscalVmOutput
{
/// <summary>
/// Id na Nota Fiscal no Banco de Dados
/// </summary>
public string id { get; set; }
/// <summary>
/// Referência para a venda que originou a nota fiscal
/// </summary>
public VendaVmOutput Venda { get; set; }
/// <summary>
/// Valor total do ICMS
/// </summary>
public decimal valorTotalICMS { get; set; }
/// <summary>
/// Valor total da nota fiscal
/// </summary>
public decimal valorTotalNotaFiscal { get; set; }
}
}
…Api.Erp\Api.Erp.Shared\ViewModels\VendaVmInput.cs
using NJsonSchema.Annotations;
namespace Api.Erp.Shared.ViewModels
{
/// <summary>
/// Informações para geração de uma nova venda
/// </summary>
public class VendaVmInput
{
/// <summary>
/// Id na venda no banco de dados
/// </summary>
[JsonSchemaExtensionData("example", "3")]
public string id { get; set; }
/// <summary>
/// Id do cliente da venda
/// </summary>
[JsonSchemaExtensionData("example", "10")]
public string idCliente { get; set; }
/// <summary>
/// Valor total da venda. Formato 00000.00
/// </summary>
[JsonSchemaExtensionData("example", 890.50)]
public decimal valorTotal { get; set; }
}
}
…Api.Erp\Api.Erp.Shared\ViewModels\VendaVmOutput.cs
namespace Api.Erp.Shared.ViewModels
{
/// <summary>
/// Informações da venda
/// </summary>
public class VendaVmOutput
{
/// <summary>
/// Id na venda no banco de dados
/// </summary>
public string id { get; set; }
/// <summary>
/// Referência para o cliente, para o qual foi executada a venda
/// </summary>
public ClienteVM cliente { get; set; }
/// <summary>
/// Valor total da venda
/// </summary>
public decimal valorTotal { get; set; }
}
}
As informações incluídas na anotação [JsonSchemaExtensionData("example", "value")] serão utilizadas para a geração dos exemplos de dados na interface do Swagger, porém, para aproveitar as informações incluídas na tag summary, precisamos configurar os nossos projetos para gerar o arquivo XML da documentação (XML Documentation file). Uma maneira muito simples de fazer isso, é editar os arquivos de nossos projetos (extensão .csproj) e incluir as instruções:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
Portanto, dê um clique duplo em cada um dos arquivos de cada projeto (.csproj) e deixe-os da seguinte forma:
…Api.Erp\Api.Erp.Clientes\Api.Erp.Clientes.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Api.Erp.Shared\Api.Erp.Shared.csproj" />
</ItemGroup>
</Project>
…Api.Erp\Api.Erp.Comercial\Api.Erp.Comercial.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Api.Erp.Shared\Api.Erp.Shared.csproj" />
</ItemGroup>
</Project>
…Api.Erp\Api.Erp.Fiscal\Api.Erp.Fiscal.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Api.Erp.Shared\Api.Erp.Shared.csproj" />
</ItemGroup>
</Project>
…Api.Erp\Api.Erp.Shared\Api.Erp.Shared.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NSwag.AspNetCore" Version="13.3.0" />
</ItemGroup>
</Project>
Ao executarmos nossas APIs, agora teremos uma riqueza muito maior de detalhes, com instruções precisas para os usuários.
Veja novamente o exemplo do endpoit utilizado para criar uma nova nota fiscal, após as modificações:
Com isto concluímos a parte 2 desta série de artigos.
Obtenha o código completo da solução até o presente momento neste repositório do GitHub.
Links para a série completa:
Criando Web APIs modernas, autônomas e rastreáveis com .Net Core utilizando arquitetura de microsserviços (Parte 3) – Implementando rastreamento distribuído
Top comments (0)