Tutorial: Generación de un cliente de API de REST

Una aplicación que consume una API de REST es un escenario muy común. Normalmente, debe generar código de cliente que la aplicación puede usar para llamar a la API de REST. En este tutorial, aprenderá a generar automáticamente el cliente de API de REST durante el proceso de compilación mediante MSBuild. Usará NSwag, una herramienta que genera código de cliente para una API de REST.

El código de ejemplo completo está disponible en Generación de clientes de API de REST en el repositorio de ejemplos de .NET en GitHub.

En el ejemplo se muestra una aplicación de consola que consume la API de Pet Store pública, que publica una especificación de OpenAPI.

En el tutorial se asume que dispone de conocimientos básicos sobre los términos de MSBuild, como tareas, destinos, propiedades o entornos de ejecución; para obtener los conocimientos necesarios, vea el artículo Conceptos de MSBuild.

Si desea ejecutar una herramienta de línea de comandos como parte de una compilación, debe tener en cuenta dos enfoques. Uno consiste en usar la tarea Exec de MSBuild, que permite ejecutar una herramienta de línea de comandos y especificar sus parámetros. El otro método consiste en crear una tarea personalizada derivada de ToolTask, lo que proporciona un mayor control.

Requisitos previos

Debe comprender los conceptos de MSBuild, como tareas, destinos y propiedades. Vea Conceptos de MSBuild.

Los ejemplos requieren MSBuild, que se instala con Visual Studio, pero también se puede instalar por separado. Vea Descargar MSBuild sin Visual Studio.

Opción 1: Tarea Exec

La tarea Exec simplemente invoca el proceso especificado con los argumentos especificados, espera a que se complete y, después, devuelve true si el proceso se ha completado correctamente y false si se produce un error.

MSBuild puede usar la generación de código de NSwag; vea NSwag.MSBuild.

El código completo está en la carpeta PetReaderExecTaskExample; puede realizar la descarga y echar un vistazo. En este tutorial, irá paso a paso y aprenderá los conceptos a lo largo del proceso.

  1. Cree una aplicación de consola llamada PetReaderExecTaskExample. Use .NET 6.0 o posterior.

  2. Cree otro proyecto en la misma solución: PetShopRestClient (esta solución va a contener el cliente generado como una biblioteca). Para este proyecto, use .NET Standard 2.1. El cliente generado no se compila en .NET Standard 2.0.

  3. En el proyecto PetReaderExecTaskExample, agregue una dependencia de proyecto al proyecto PetShopRestClient.

  4. En el proyecto PetShopRestClient, incluya los siguientes paquetes NuGet:

    • Nswag.MSBuild, que permite acceder al generador de código desde MSBuild.
    • Newtonsoft.Json, que es necesario para compilar el cliente generado.
    • System.ComponentModel.Annotations, que es necesario para compilar el cliente generado.
  5. En el proyecto PetShopRestClient, agregue una carpeta (denominada PetShopRestClient) para la generación de código y elimine el archivo Class1.cs que se generó automáticamente.

  6. Cree un archivo de texto denominado petshop-openapi-spec.json en la raíz del proyecto. Copie la especificación de OpenApi desde aquí y guárdela en el archivo. Es mejor copiar una instantánea de la especificación en lugar de leerla en línea durante la compilación. Siempre es conveniente tener una compilación reproducible de forma coherente que dependa solo de la entrada. El consumo directo de la API podría transformar una compilación que hoy funciona en una compilación pero que mañana produce un error desde el mismo origen. La instantánea guardada en petshop-openapi-spec.json nos permitirá seguir teniendo una versión que se compila incluso si cambia la especificación.

  7. A continuación, modifique PetShopRestClient.csproj y agregue un destino de MSBuild para generar el cliente durante el proceso de compilación.

    En primer lugar, agregue algunas propiedades útiles para la generación de clientes:

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

    Agregue los siguientes 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 este destino usa los atributos BeforeTarget y AfterTarget como una forma de definir el orden de compilación. El primer destino llamado generatePetClient se ejecutará antes del destino de compilación principal, por lo que el origen se creará antes de que se ejecute el compilador. El parámetro de entrada y salida está relacionado con la compilación incremental. MSBuild compara las marcas de tiempo de los archivos de entrada con las de los archivos de salida y determina si debe omitir, compilar o recompilar parcialmente un destino.

    Después de instalar el paquete NuGet NSwag.MSBuild en el proyecto, puede usar la variable $(NSwagExe) en el archivo .csproj para ejecutar la herramienta de línea de comandos NSwag en un destino de MSBuild. De este modo, las herramientas se pueden actualizar fácilmente a través de NuGet. En este caso, usa la tarea Exec de MSBuild para ejecutar el programa de NSwag con los parámetros necesarios para generar la API de REST del cliente. Vea Comandos y parámetros de NSwag.

    Puede capturar la salida de <Exec> al agregar ConsoleToMsBuild="true" a la etiqueta <Exec> y, a continuación, capturar la salida mediante el parámetro ConsoleOutput en una etiqueta <Output>. ConsoleOutput devuelve la salida como un elemento Item. Se recortan los espacios en blanco. ConsoleOutput se habilita cuando ConsoleToMSBuild es true.

    El segundo destino denominado forceReGenerationOnRebuild elimina la clase generada durante la limpieza para forzar la regeneración del código generado durante la ejecución del destino de recompilación. Este destino se ejecuta después del destino predefinido de MSBuild CoreClean.

  8. Ejecute una recompilación de la solución de Visual Studio y consulte el cliente generado en la carpeta PetShopRestClient.

  9. Ahora, use el cliente generado. Vaya al cliente Program.cs y copie el código siguiente:

    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}");
           }
       }
    }
    

    Nota:

    Este código usa new HttpClient() porque es fácil de demostrar, pero no es el procedimiento recomendado para el código real. El procedimiento recomendado es usar HttpClientFactory para crear un objeto HttpClient que solucione los problemas conocidos de la solicitud HttpClient, como el agotamiento de recursos o los problemas de DNS obsoleto. Vea Uso de IHttpClientFactory para implementar solicitudes HTTP resistentes.

Felicidades. Ahora, puede ejecutar el programa para ver cómo funciona.

Opción 2: Tarea personalizada derivada de ToolTask

En muchos casos, usar la tarea Exec es lo suficientemente conveniente como para ejecutar una herramienta externa para hacer algo parecido a la generación de código de clientes de API de REST, pero ¿qué ocurre si desea permitir la generación de código de clientes de API de REST si no usa una ruta de acceso de Windows absoluta como entrada? O bien, ¿qué ocurre si necesita calcular de alguna manera dónde está el archivo ejecutable? Cuando haya alguna situación en la que necesite ejecutar código para realizar trabajo adicional, la tarea de herramienta de MSBuild es la mejor solución. La clase ToolTask es una clase abstracta derivada de Task de MSBuild. Puede definir una subclase concreta, que crea una tarea personalizada de MSBuild. Este enfoque le permite ejecutar cualquier código necesario para prepararse para la ejecución de comandos. Debe leer primero el tutorial Creación de una tarea personalizada para la generación de código.

Creará una tarea personalizada derivada de MSBuild ToolTask que generará un cliente de API de REST, pero se diseñará para emitir un error si intenta hacer referencia a la especificación de OpenApi mediante una dirección http. NSwag admite una dirección http como una entrada de la especificación de OpenApi, pero para los fines de este ejemplo, supongamos que hay un requisito de diseño para no permitirlo.

El código completo está en esta carpeta PetReaderToolTaskExample; puede realizar la descarga y echar un vistazo. En este tutorial, irá paso a paso y aprenderá algunos conceptos que puede aplicar a sus propios escenarios.

  1. Cree un proyecto de Visual Studio para la tarea personalizada. Asígnele el nombre RestApiClientGenerator y use la plantilla de la biblioteca de clases (C#) con .NET Standard 2.0. Asigne a la solución el nombre PetReaderToolTaskExample.

  2. Elimine Class1.cs, que se generó automáticamente.

  3. Agregar los paquetes NuGet Microsoft.Build.Utilities.Core:

    • Cree una clase llamada RestApiClientGenerator.

    • Herede de ToolTask de MSBuild e implemente el método abstract como se muestra en el código siguiente:

      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. Agregue los siguientes parámetros:

    • InputOpenApiSpec, donde se encuentra la especificación.
    • ClientClassName, nombre de la clase generada.
    • ClientNamespaceName, espacio de nombres donde se genera la clase.
    • FolderClientClass, ruta de acceso a la carpeta donde se ubicará la clase.
    • NSwagCommandFullPath, ruta de acceso completa al directorio donde se encuentra NSwag.exe.
         [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 la herramienta de línea de comandos NSwag. Necesitará la ruta de acceso completa al directorio donde se encuentra NSwag.exe.

  6. Implemente los métodos abstract:

       protected override string ToolName => "RestApiClientGenerator";
    
       protected override string GenerateFullPathToTool()
       {
           return $"{NSwagCommandFullPath}\\NSwag.exe";
       }
    
  7. Hay muchos métodos que puede reemplazar. Para la implementación actual, defina estos dos:

    • Defina el parámetro command:
      protected override string GenerateCommandLineCommands()
      {
          return $"openapi2csclient /input:{InputOpenApiSpec}  /classname:{ClientClassName} /namespace:{ClientNamespaceName} /output:{FolderClientClass}\\{ClientClassName}.cs";
      }
    
    • Validación de parámetros:
    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;
    }
    

    Nota:

    Esta validación simple podría realizarse de otro modo en el archivo de MSBuild, pero se recomienda hacerla en el código de C# y encapsular el comando y la lógica.

  8. Compile el proyecto.

Creación de una aplicación de consola para usar la nueva tarea de MSBuild

El siguiente paso consiste en crear una aplicación que use la tarea.

  1. Cree un proyecto de aplicación de consola y asígnele el nombre PetReaderToolTaskConsoleApp. Elija .NET 6.0. Márquelo como proyecto de inicio.

  2. Cree un proyecto de biblioteca de clases para generar el código, denominado PetRestApiClient. Use .NET Standard 2.1.

  3. En el proyecto PetReaderToolTaskConsoleApp, cree una dependencia de proyecto en PetRestApiClient.

  4. En el proyecto PetRestApiClient, cree una carpeta PetRestApiClient. Esta carpeta contendrá el código generado.

  5. Elimine Class1.cs, que se generó automáticamente.

  6. En PetRestApiClient, agregue los siguientes paquetes NuGet:

    • Newtonsoft.Json, que es necesario para compilar el cliente generado.
    • System.ComponentModel.Annotations, que es necesario para compilar el cliente generado.
  7. En el proyecto PetRestApiClient, cree un archivo de texto denominado petshop-openapi-spec.json (en la carpeta del proyecto). Para agregar la especificación de OpenApi, copie el contenido de aquí en el archivo. Preferimos una compilación reproducible que dependa solo de la entrada, como se ha comentado anteriormente. En este ejemplo, se producirá un error de compilación si un usuario elige una dirección URL como entrada de la especificación de OpenApi.

    Importante

    Una recompilación general no funcionará. Verá errores que indican que no se puede copiar ni eliminar RestApiClientGenerator.dll'. Esto se debe a que está intentando compilar la tarea personalizada de MBuild en el mismo proceso de compilación que la usa. Seleccione PetReaderToolTaskConsoleApp y recompile solo ese proyecto. La otra solución consiste en colocar la tarea personalizada en una solución de Visual Studio independiente, como hizo en el ejemplo de Tutorial: Creación de una tarea personalizada.

  8. Copie el siguiente código en 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. Cambie las instrucciones de MSBuild para llamar a la tarea y generar el código. Edite PetRestApiClient.csproj siguiendo estos pasos:

    1. Registre el uso de tarea personalizada de MSBuild:

      <UsingTask TaskName="RestApiClientGenerator.RestApiClientGenerator" AssemblyFile="..\RestApiClientGenerator\bin\Debug\netstandard2.0\RestApiClientGenerator.dll" />
      
    2. Agregue algunas propiedades necesarias para ejecutar la tarea:

       <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

      Seleccione el valor NSwagCommandFullPath adecuado en función de la ubicación de instalación del sistema.

    3. Agregue un destino de MSBuild para generar el cliente durante el proceso de compilación. Este destino debe ejecutarse antes de que se ejecute CoreCompile para generar el código usado en la compilación.

      <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 y Output están relacionados con la compilación incremental, y el destino forceReGenerationOnRebuild elimina el archivo generado después de CoreClean, lo que obliga al cliente a volver a generarse durante la operación de recompilación.

  10. Seleccione PetReaderToolTaskConsoleApp y recompile solo ese proyecto. Ahora, se genera el código de cliente, y el código se compila. Puede ejecutarlo y ver cómo funciona. Este código genera el código a partir de un archivo, y eso se permite.

  11. En este paso, demostrará la validación de parámetros. En PetRestApiClient.csproj, cambie la propiedad $(PetClientInputOpenApiSpec) para usar la dirección URL:

      <PetClientInputOpenApiSpec>https://petstore.swagger.io/v2/swagger.json</PetClientInputOpenApiSpec>
    
  12. Seleccione PetReaderToolTaskConsoleApp y recompile solo ese proyecto. Se producirá el error "No se permite la dirección URL" de acuerdo con el requisito de diseño.

Descargar el código

Instale la herramienta de línea de comandos NSwag. A continuación, necesitará la ruta de acceso completa al directorio donde se encuentra NSwag.exe. Después, edite PetRestApiClient.csproj y seleccione el valor $(NSwagCommandFullPath) adecuado en función de la ruta de instalación del equipo. Ahora, seleccione RestApiClientGenerator y compile solo ese proyecto y, por último, seleccione y recompile PetReaderToolTaskConsoleApp. Puede ejecutar PetReaderToolTaskConsoleApp para comprobar que todo funciona según lo previsto.

Pasos siguientes

Es posible que quiera publicar la tarea personalizada como un paquete NuGet.

O bien, aprenda a probar una tarea personalizada.