Share via


Tutorial: gerar um cliente da API REST

Um aplicativo que consome uma API REST é um cenário muito comum. Normalmente, você precisa gerar o código do cliente que seu aplicativo pode usar para chamar a API REST. Neste tutorial, você aprenderá a gerar o cliente da API REST automaticamente durante o processo de build usando o MSBuild. Você usará o NSwag, uma ferramenta que gera código do cliente para uma API REST.

O código de exemplo completo está disponível na geração de cliente da API REST, no repositório de exemplos do .NET no GitHub.

O exemplo mostra um aplicativo de console que consome a API pública Pet Store, que publica uma especificação OpenAPI.

O tutorial pressupõe conhecimento básico dos termos do MSBuild, como tarefas, destinos, propriedades ou runtimes; para obter o contexto necessário, confira o artigo Conceitos do MSBuild.

Quando você deseja executar uma ferramenta de linha de comando como parte de um build, há duas abordagens a serem consideradas. Uma delas é usar a tarefa Exec do MSBuild, que permite executar uma ferramenta de linha de comando e especificar os parâmetros dela. O outro método é criar uma tarefa personalizada derivada de ToolTask, que oferece maior controle.

Pré-requisitos

Você deve ter uma compreensão dos conceitos do MSBuild, como tarefas, destinos e propriedades. Confira Conceitos do MSBuild.

Os exemplos exigem o MSBuild, que é instalado com o Visual Studio, mas também pode ser instalado separadamente. Confira Baixar o MSBuild sem o Visual Studio.

Opção 1: tarefa Exec

A tarefa Exec simplesmente invoca o processo especificado com os argumentos especificados, aguarda que ele seja concluído e, em seguida, retorna true se o processo é concluído com êxito e false se um erro ocorre.

A geração de código NSwag pode ser usada do MSBuild; confira NSwag.MSBuild.

O código completo está na pasta PetReaderExecTaskExample; você pode baixar e dar uma olhada. Você percorrerá este tutorial passo a passo e aprenderá os conceitos durante o processo.

  1. Crie um aplicativo de console chamado PetReaderExecTaskExample. Use o .NET 6.0 ou posterior.

  2. Crie outro projeto na mesma solução: PetShopRestClient (Essa solução conterá o cliente gerado como uma biblioteca). Para este projeto, use o .NET Standard 2.1. O cliente gerado não é compilado no .NET Standard 2.0.

  3. No projeto PetReaderExecTaskExample, adicione uma dependência de projeto ao projeto PetShopRestClient.

  4. No projeto PetShopRestClient, inclua os seguintes pacotes NuGet:

    • Nswag.MSBuild, que permite o acesso ao gerador de código do MSBuild
    • Newtonsoft.Json, necessário para compilar o cliente gerado
    • System.ComponentModel.Annotations, necessário para compilar o cliente gerado
  5. No projeto PetShopRestClient, adicione uma pasta (chamada PetShopRestClient) para a geração de código e exclua o Class1.cs que foi gerado automaticamente.

  6. Crie um arquivo de texto chamado petshop-openapi-spec.json na raiz do projeto. Copie a especificação do OpenApi aqui e salve-a no arquivo. É melhor copiar um instantâneo da especificação em vez de lê-lo online durante o build. Você deve buscar sempre um build consistentemente reproduzível que dependa apenas da entrada. Consumir a API diretamente pode transformar um build que funciona hoje em um build que falha amanhã, usando a mesma origem. O instantâneo salvo em petshop-openapi-spec.json nos permitirá ainda ter uma versão que seja compilada mesmo se a especificação for alterada.

  7. Em seguida, modifique PetShopRestClient.csproj e adicione destinos do MSBuild para gerar o cliente durante o processo de build.

    Primeiro, adicione algumas propriedades úteis para a geração do cliente:

     <PropertyGroup>
         <PetOpenApiSpecLocation>petshop-openapi-spec.json</PetOpenApiSpecLocation>
         <PetClientClassName>PetShopRestClient</PetClientClassName>
         <PetClientNamespace>PetShopRestClient</PetClientNamespace>
         <PetClientOutputDirectory>PetShopRestClient</PetClientOutputDirectory>
     </PropertyGroup>
    

    Adicione os seguintes destinos:

     <Target Name="generatePetClient" BeforeTargets="CoreCompile" Inputs="$(PetOpenApiSpecLocation)" Outputs="$(PetClientOutputDirectory)\$(PetClientClassName).cs">
         <Exec Command="$(NSwagExe) openapi2csclient /input:$(PetOpenApiSpecLocation)  /classname:$(PetClientClassName) /namespace:$(PetClientNamespace) /output:$(PetClientOutputDirectory)\$(PetClientClassName).cs" ConsoleToMSBuild="true">
         <Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
       </Exec>
     </Target>
     <Target Name="forceReGenerationOnRebuild" AfterTargets="CoreClean">
        <Delete Files="$(PetClientOutputDirectory)\$(PetClientClassName).cs"></Delete>
     </Target>
    

    Observe que esse destino usa os atributos BeforeTarget e AfterTarget como forma de definir a ordem de build. O primeiro destino chamado generatePetClient será executado antes do destino de build principal, portanto, a origem será criada antes da execução do compilador. O parâmetro de entrada e saída está relacionado ao Build Incremental. O MSBuild pode comparar os carimbos de data/hora dos arquivos de entrada com os carimbos de data/hora dos arquivos de saída e determinar se deve ignorar, compilar ou recompilar parcialmente um destino.

    Depois de instalar o pacote NuGet NSwag.MSBuild em seu projeto, você pode usar a variável $(NSwagExe) em seu arquivo .csproj para executar a ferramenta de linha de comando NSwag em um destino do MSBuild. Dessa forma, as ferramentas podem ser facilmente atualizadas por meio do NuGet. Aqui, você está usando a tarefa MSBuild Exec para executar o programa NSwag com os parâmetros necessários para gerar a API REST do cliente. Confira Comando NSwag e parâmetros.

    Você pode capturar a saída da adição de ConsoleToMsBuild="true" por <Exec> à marca <Exec> e, em seguida, capturar a saída usando o parâmetro ConsoleOutput em uma marca <Output>. ConsoleOutput retorna a saída como um Item. O espaço em branco é cortado. ConsoleOutput será habilitado quando ConsoleToMSBuild for true.

    O segundo destino chamado forceReGenerationOnRebuild exclui a classe gerada durante a limpeza para forçar a regeneração do código gerado durante a execução do destino de rebuild. Esse destino é executado após o destino predefinido CoreClean do MSBuild.

  8. Execute um rebuild da Solução do Visual Studio e veja o cliente gerado na pasta PetShopRestClient.

  9. Agora, use o cliente gerado. Acesse o cliente Program.cs e copie o seguinte código:

    using System;
    using System.Net.Http;
    
    namespace PetReaderExecTaskExample
    {
       internal class Program
       {
           private const string baseUrl = "https://petstore.swagger.io/v2";
           static void Main(string[] args)
           {
               HttpClient httpClient = new HttpClient();
               httpClient.BaseAddress = new Uri(baseUrl);
               var petClient = new PetShopRestClient.PetShopRestClient(httpClient);
               var pet = petClient.GetPetByIdAsync(1).Result;
               Console.WriteLine($"Id: {pet.Id} Name: {pet.Name} Status: {pet.Status} CategoryName: {pet.Category.Name}");
           }
       }
    }
    

    Observação

    Esse código usa new HttpClient() porque é simples de demonstrar, mas não é a melhor prática para código do mundo real. A melhor prática é usar HttpClientFactory para criar um objeto HttpClient que resolva os problemas conhecidos da solicitação HttpClient, como esgotamento de recursos ou problemas de DNS obsoletos. Confira Usar IHttpClientFactory para implementar solicitações HTTP resilientes.

Parabéns! Agora, você pode executar o programa para ver como ele funciona.

Opção 2: tarefa personalizada derivada de ToolTask

Em muitos casos, usar a tarefa Exec é bom o suficiente para executar uma ferramenta externa para fazer algo como a geração de código do cliente da API REST, mas e se você quiser permitir a geração de código do cliente da API REST se e somente se você não usar um caminho absoluto do Windows como entrada? Ou e se você precisar calcular, de alguma forma, onde o executável está? Quando há alguma situação em que você precisa executar algum código para fazer um trabalho extra, a ToolTask do MSBuild é a melhor solução. A classe ToolTask é uma classe abstrata derivada de Task do MSBuild. Você pode definir uma subclasse concreta, que cria uma tarefa personalizada do MSBuild. Essa abordagem permite que você execute qualquer código necessário para se preparar para a execução do comando. Você deve primeiro ler o tutorial Criar uma tarefa personalizada para a geração de código.

Você criará uma tarefa personalizada derivada de ToolTask do MSBuild, que gerará um cliente da API REST, mas ela será projetada para emitir um erro se você tentar referenciar a especificação OpenApi usando um endereço http. O NSwag dá suporte a um endereço http como entrada de especificação OpenApi, mas para os fins deste exemplo, vamos supor que haja um requisito de design para não permitir isso.

O código completo está nessa pasta PetReaderToolTaskExample; você pode baixar e dar uma olhada. Neste tutorial, você passará passo a passo e aprenderá alguns conceitos que podem ser aplicados aos seus cenários.

  1. Crie um projeto do Visual Studio para a tarefa personalizada. Chame-o RestApiClientGenerator e use o modelo Biblioteca de Classes (C#) com o .NET Standard 2.0. Nomeie a solução PetReaderToolTaskExample.

  2. Exclua Class1.cs, que foi gerado automaticamente.

  3. Adicione os pacotes NuGet Microsoft.Build.Utilities.Core:

    • Crie uma classe chamada RestApiClientGenerator

    • Herde do ToolTask do MSBuild e implemente o método abstrato, conforme mostrado no seguinte código:

      using Microsoft.Build.Utilities;
      
      namespace RestApiClientGenerator
      {
          public class RestApiClientGenerator : ToolTask
          {
              protected override string ToolName => throw new System.NotImplementedException();
      
              protected override string GenerateFullPathToTool()
              {
                  throw new System.NotImplementedException();
              }
          }
      }
      
  4. Adicione os parâmetros a seguir:

    • InputOpenApiSpec, em que a especificação está
    • ClientClassName, nome da classe gerada
    • ClientNamespaceName, namespace em que a classe é gerada
    • FolderClientClass, caminho para a pasta em que a classe estará localizada
    • NSwagCommandFullPath, caminho completo para o diretório em que NSwag.exe está localizado
         [Required]
         public string InputOpenApiSpec { get; set; }
         [Required]
         public string ClientClassName { get; set; }
         [Required]
         public string ClientNamespaceName { get; set; }
         [Required]
         public string FolderClientClass { get; set; }
         [Required]
         public string NSwagCommandFullPath { get; set; }
    
  5. Instale a ferramenta de linha de comando NSwag. Você precisará do caminho completo para o diretório em que NSwag.exe está localizado.

  6. Implemente os métodos abstratos:

       protected override string ToolName => "RestApiClientGenerator";
    
       protected override string GenerateFullPathToTool()
       {
           return $"{NSwagCommandFullPath}\\NSwag.exe";
       }
    
  7. Há muitos métodos que você pode substituir. Para a implementação atual, defina estes dois:

    • Defina o parâmetro de comando:
      protected override string GenerateCommandLineCommands()
      {
          return $"openapi2csclient /input:{InputOpenApiSpec}  /classname:{ClientClassName} /namespace:{ClientNamespaceName} /output:{FolderClientClass}\\{ClientClassName}.cs";
      }
    
    • Validação de parâmetro:
    protected override bool ValidateParameters()
    {
          //http address is not allowed
          var valid = true;
          if (InputOpenApiSpec.StartsWith("http:") || InputOpenApiSpec.StartsWith("https:"))
          {
              valid = false;
              Log.LogError("URL is not allowed");
          }
    
          return valid;
    }
    

    Observação

    Essa validação simples pode ser feita de outra forma no arquivo MSBuild, mas é recomendável fazer isso no código C# e encapsular o comando e a lógica.

  8. Compile o projeto.

Criar um aplicativo de console para usar a nova tarefa do MSBuild

A próxima etapa é criar um aplicativo que usa a tarefa.

  1. Crie um projeto de Aplicativo de Console e nomeie-o PetReaderToolTaskConsoleApp. Escolha o .NET 6.0. Marque-o como projeto de inicialização.

  2. Crie um projeto de Biblioteca de Classes chamado PetRestApiClient para gerar o código. Use o .NET Standard 2.1.

  3. No projeto PetReaderToolTaskConsoleApp, crie uma dependência de projeto para PetRestApiClient.

  4. No projeto PetRestApiClient, crie uma pasta PetRestApiClient. Essa pasta conterá o código gerado.

  5. Exclua Class1.cs, que foi gerado automaticamente.

  6. No PetRestApiClient, adicione os seguintes pacotes NuGet:

    • Newtonsoft.Json, necessário para compilar o cliente gerado
    • System.ComponentModel.Annotations, necessário para compilar o cliente gerado
  7. No projeto PetRestApiClient, crie um arquivo de texto chamado petshop-openapi-spec.json (na pasta do projeto). Para adicionar a especificação OpenApi, copie o conteúdo daqui para o arquivo. Gostamos de um build reproduzível que depende apenas da entrada, conforme discutido anteriormente. Neste exemplo, você gerará um erro de build se um usuário escolher uma URL como a entrada de especificação OpenApi.

    Importante

    Um rebuild não funcionará. Você verá erros que indicam que não é possível copiar ou excluir RestApiClientGenerator.dll'. Isso ocorre porque ele está tentando criar a tarefa personalizada do MBuild no mesmo processo de build que o usa. Selecione PetReaderToolTaskConsoleApp e recompile somente esse projeto. A outra solução é colocar a tarefa personalizada em uma solução totalmente independente do Visual Studio, como você fez no exemplo Tutorial: Criar uma tarefa personalizada.

  8. Copie o seguinte código para Program.cs:

     using System;
     using System.Net.Http;
     namespace PetReaderToolTaskConsoleApp
     {
       internal class Program
       {
           private const string baseUrl = "https://petstore.swagger.io/v2";
           static void Main(string[] args)
           {
               HttpClient httpClient = new HttpClient();
               httpClient.BaseAddress = new Uri(baseUrl);
               var petClient = new PetRestApiClient.PetRestApiClient(httpClient);
               var pet = petClient.GetPetByIdAsync(1).Result;
               Console.WriteLine($"Id: {pet.Id} Name: {pet.Name} Status: {pet.Status} CategoryName: {pet.Category.Name}");
           }
       }
     }
    
  9. Altere as instruções do MSBuild para chamar a tarefa e gerar o código. Edite PetRestApiClient.csproj seguindo estas etapas:

    1. Registre o uso da tarefa personalizada do MSBuild:

      <UsingTask TaskName="RestApiClientGenerator.RestApiClientGenerator" AssemblyFile="..\RestApiClientGenerator\bin\Debug\netstandard2.0\RestApiClientGenerator.dll" />
      
    2. Adicione algumas propriedades necessárias para executar a tarefa:

       <PropertyGroup>
          <!--The place where the OpenApi spec is in-->
         <PetClientInputOpenApiSpec>petshop-openapi-spec.json</PetClientInputOpenApiSpec>
         <PetClientClientClassName>PetRestApiClient</PetClientClientClassName>
         <PetClientClientNamespaceName>PetRestApiClient</PetClientClientNamespaceName>
         <PetClientFolderClientClass>PetRestApiClient</PetClientFolderClientClass>
         <!--The directory where NSawg.exe is in-->
         <NSwagCommandFullPath>C:\Nsawg\Win</NSwagCommandFullPath>
        </PropertyGroup>
      

      Importante

      Selecione o valor adequado de NSwagCommandFullPath com base no local de instalação em seu sistema.

    3. Adicione um destino do MSBuild para gerar o cliente durante o processo de build. Esse destino deve ser executado antes de CoreCompile ser executado para gerar o código usado na compilação.

      <Target Name="generatePetClient" BeforeTargets="CoreCompile" Inputs="$(PetClientInputOpenApiSpec)" Outputs="$(PetClientFolderClientClass)\$(PetClientClientClassName).cs">
        <!--Calling our custom task derivated from MSBuild Tool Task-->
        <RestApiClientGenerator InputOpenApiSpec="$(PetClientInputOpenApiSpec)" ClientClassName="$(PetClientClientClassName)" ClientNamespaceName="$(PetClientClientNamespaceName)" FolderClientClass="$(PetClientFolderClientClass)" NSwagCommandFullPath="$(NSwagCommandFullPath)"></RestApiClientGenerator>
      </Target>
      
      <Target Name="forceReGenerationOnRebuild" AfterTargets="CoreClean">
        <Delete Files="$(PetClientFolderClientClass)\$(PetClientClientClassName).cs"></Delete>
      </Target>
      

    Input e Output estão relacionados ao Build Incremental, e o destino forceReGenerationOnRebuild exclui o arquivo gerado após CoreClean, o que força o cliente a ser regenerado durante a operação de rebuild.

  10. Selecione PetReaderToolTaskConsoleApp e recompile somente esse projeto. Agora, o código do cliente é gerado e o código é compilado. Você pode executá-lo e ver como ele funciona. Esse código gera o código de um arquivo, e isso é permitido.

  11. Nesta etapa, você demonstrará a validação do parâmetro. Em PetRestApiClient.csproj, altere a propriedade $(PetClientInputOpenApiSpec) para usar a URL:

      <PetClientInputOpenApiSpec>https://petstore.swagger.io/v2/swagger.json</PetClientInputOpenApiSpec>
    
  12. Selecione PetReaderToolTaskConsoleApp e recompile somente esse projeto. Você receberá o erro "URL não é permitida" de acordo com o requisito de design.

Baixar o código

Instale a ferramenta de linha de comando NSwag. Depois, você precisará do caminho completo para o diretório em que NSwag.exe está localizado. Depois disso, edite PetRestApiClient.csproj e selecione o valor adequado $(NSwagCommandFullPath) com base no caminho de instalação no computador. Agora, selecione e compile RestApiClientGenerator somente esse projeto e, por fim, selecione e recompile PetReaderToolTaskConsoleApp. Você pode executar PetReaderToolTaskConsoleApp. Para verificar se tudo funciona conforme o esperado.

Próximas etapas

Talvez você queira publicar sua tarefa personalizada como um pacote NuGet.

Ou saiba como testar uma tarefa personalizada.