Учебник. Создание клиента REST API

Приложение, использующее REST API, является очень распространенным сценарием. Как правило, необходимо создать клиентский код, который приложение может использовать для вызова REST API. В этом учебнике вы узнаете, как создавать клиент REST API автоматически во время процесса сборки с помощью MSBuild. Вы будете использовать NSwag — средство, которое создает клиентский код для REST API.

Полный пример кода для .NET см. в репозитории Создание клиента REST API на GitHub.

В примере показано консольное приложение, которое использует общедоступный Pet Store API, который публикует спецификацию OpenAPI.

В учебнике предполагается, что вы знаете основные понятия MSBuild, такие как задачи, целевые объекты, свойства и среды выполнения. Необходимые сведения см. в статье Основные понятия MSBuild.

Если вы хотите запустить программу командной строки как часть сборки, рассмотрите два подхода. Один подход — использовать задачу Exec MSBuild, которая позволяет запустить программу командной строки и указать ее параметры. Другой — создать настраиваемую задачу, производную от ToolTask, которая обеспечивает более детальный контроль.

Необходимые компоненты

Вы должны быть знакомы с основными понятиями MSBuild, такими как задачи, целевые объекты и свойства. См. Основные понятия MSBuild.

Для этих примеров требуется платформа MSBuild. Она устанавливается вместе с Visual Studio, но ее также можно установить отдельно. См. Скачивание MSBuild без Visual Studio.

Вариант 1. Задача Exec

Задача Exec просто вызывает указанный процесс с указанными аргументами, дожидается его завершения, а затем возвращает значение true, если процесс успешно завершен, или false, если произошла ошибка.

Создание кода NSwag можно использовать из MSBuild. См. NSwag.MSBuild.

Полный код находится в папке PetReaderExecTaskExample. Вы можете загрузить и изучить его. В этом учебнике вы будете выполнять пошаговые инструкции и ознакомитесь с основными понятиями.

  1. Создание нового консольного приложения с именем PetReaderExecTaskExample. Используйте .NET 6.0 или более поздней версии.

  2. Создайте другой проект в том же решении: PetShopRestClient (это решение будет содержать созданный клиент как библиотеку). Для этого проекта используйте .NET Standard 2.1. Созданный клиент не компилируется на .NET Standard 2.0.

  3. В проекте PetReaderExecTaskExample добавьте в зависимость от проекта PetShopRestClient.

  4. В проекте PetShopRestClient включите следующие пакеты NuGet:

    • Nswag.MSBuild, который обеспечивает доступ к генератору кода из MSBuild;
    • Newtonsoft.Json, необходимый для компиляции созданного клиента;
    • System.ComponentModel.Annotations, необходимый для компиляции созданного клиента.
  5. В проекте PetShopRestClient добавьте папку (с именем PetShopRestClient) для создания кода и удалите класс Class1.cs, который был создан автоматически.

  6. Создайте текстовый файл с именем petshop-openapi-spec.json в корне проекта. Скопируйте спецификацию OpenApi отсюда и сохраните ее в файле. Рекомендуется копировать моментальный снимок спецификации вместо того, чтобы читать его в сети во время сборки. Всегда рекомендуется, чтобы сборка была постоянно воспроизводимой и зависела только от входных данных. При использовании API напрямую работающая сборка может внезапно перестать работать. Моментальный снимок, сохраненный в petshop-openapi-spec.json, позволит иметь версию, позволяющую выполнить сборку даже в случае изменения спецификации.

  7. Затем измените PetShopRestClient.csproj и добавьте целевые объекты MSBuild для создания клиента во время процесса сборки.

    Сначала добавьте свойства, полезные для создания клиента:

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

    Добавьте следующие целевые объекты:

     <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>
    

    Обратите внимание, что в этом целевом объекте для определения порядка сборки используются атрибуты BeforeTarget и AfterTarget. Первый целевой объект с именем generatePetClient будет выполняться до целевого объекта компиляции ядра, поэтому источник будет создан до выполнения компилятора. Входной и выходной параметры связаны с инкрементной сборкой. MSBuild может сравнить метки времени входных файлов с метками времени выходных файлов и определить, следует ли пропустить, собрать или частично перестроить целевой объект.

    После установки пакета NuGet NSwag.MSBuild в проекте вы можете использовать переменную $(NSwagExe) в файле .csproj для запуска программы командной строки NSwag в целевом объекте MSBuild. Таким образом, средства можно легко обновить с помощью NuGet. В данном примере вы используете задачу MSBuild Exec для выполнения программы NSwag с необходимыми параметрами, чтобы создать REST API клиента. См. команду и параметры NSwag.

    Теперь вы можете захватывать выходные данные из <Exec>, добавив ConsoleToMsBuild="true" в тег <Exec>, а затем записав выходные данные с помощью параметра ConsoleOutput в тег <Output>. ConsoleOutput возвращает выходные данные в виде Item. Пробелы усекаются. ConsoleOutput включен, если параметр ConsoleToMSBuild имеет значение true.

    Второй целевой объект, вызываемый forceReGenerationOnRebuild, удаляет созданный класс во время очистки, чтобы при перестройке целевого объекта было выполнено повторное создание созданного кода. Этот целевой объект выполняется после предопределенного целевого объекта MSBuild CoreClean.

  8. Выполните перестройку решения Visual Studio и просмотрите созданный клиент в папке PetShopRestClient.

  9. Теперь используйте созданный клиент. Перейдите в Program.cs клиента и скопируйте следующий код:

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

    Примечание.

    Этот код использует new HttpClient() для наглядности, но это не лучшее решение для рабочего кода. Рекомендуется использовать HttpClientFactory для создания объекта HttpClient, который устраняет известные проблемы запроса HttpClient, такие как нехватка ресурсов или проблемы с устаревшим DNS. См. Использование IHttpClientFactory для реализации устойчивых HTTP-запросов.

Поздравляем! Теперь можно выполнить программу, чтобы посмотреть, как она работает.

Вариант 2. Настраиваемая задача, производная от ToolTask

Во многих случаях использования задачи Exec достаточно для выполнения внешнего средства, например создания клиентского кода REST API. Но что делать, если вы хотите разрешить создание клиентского кода REST API только в том случае, если вы не используете абсолютный путь Windows в качестве входных данных? Или если необходимо определить, где находится исполняемый файл? При возникновении ситуаций, в которых необходимо выполнить некоторый дополнительный код, лучше всего использовать MSBuild Tool Task. Класс ToolTask является абстрактным классом, производным от MSBuild Task. Вы можете определить конкретный подкласс, который создает настраиваемую задачу MSBuild. Такой подход позволяет выполнять любой код, необходимый для подготовки к выполнению команды. Сначала ознакомьтесь с учебником Создание настраиваемой задачи для создания кода.

Вы создадите настраиваемую задачу, производную от MSBuild ToolTask, которая создаст клиент REST API, но будет выдавать ошибку при попытке ссылки на спецификацию OpenApi с использованием HTTP-адреса. NSwag поддерживает HTTP-адрес в качестве входных данных спецификации OpenApi, но для целей этого примера предположим, что для требуется это запретить.

Полный код находится в папке PetReaderToolTaskExample. Вы можете загрузить его и изучить. В этом учебнике вы будете следовать пошаговым инструкциям и ознакомитесь с некоторыми основными понятиями, которые сможете применять к собственным сценариям.

  1. Создайте новый проект Visual Studio для настраиваемой задачи. Назовите его RestApiClientGenerator и используйте шаблон библиотеки классов (C#) с .NET Standard 2.0. Назовите решение PetReaderToolTaskExample.

  2. Удалите файл Class1.cs, который был создан автоматически.

  3. Добавьте пакеты NuGet Microsoft.Build.Utilities.Core:

    • Создайте класс RestApiClientGenerator.

    • Унаследуйте от ToolTask MSBuild и реализуйте абстрактный метод, как показано в следующем коде:

      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. Добавьте следующие параметры:

    • InputOpenApiSpec, где спецификация
    • ClientClassName — имя сформированного класса.
    • ClientNamespaceName — пространство имен, в котором создается класс.
    • FolderClientClass — путь к папке, в которой будет расположен класс.
    • NSwagCommandFullPath — полный путь к каталогу, в котором находится 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. Установите программу командной строки NSwag. Вам понадобится полный путь к каталогу, в котором находится NSwag.exe.

  6. Реализуйте абстрактные методы:

       protected override string ToolName => "RestApiClientGenerator";
    
       protected override string GenerateFullPathToTool()
       {
           return $"{NSwagCommandFullPath}\\NSwag.exe";
       }
    
  7. Существует множество методов, которые можно переопределить. Для текущей реализации определите следующие два:

    • Определит параметр команды:
      protected override string GenerateCommandLineCommands()
      {
          return $"openapi2csclient /input:{InputOpenApiSpec}  /classname:{ClientClassName} /namespace:{ClientNamespaceName} /output:{FolderClientClass}\\{ClientClassName}.cs";
      }
    
    • Проверка параметров:
    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;
    }
    

    Примечание.

    Такую простую проверку можно выполнить другим способом в файле MSBuild, но рекомендуется выполнить ее в коде C# и инкапсулировать команду и логику.

  8. Выполните сборку проекта.

Создание консольного приложения для использования новой задачи MSBuild

Следующим шагом является создание приложения, использующего эту задачу.

  1. Создайте проект консольного приложения и назовите его PetReaderToolTaskConsoleApp. Выберите .NET 6.0. Назначьте его запускаемым проектом.

  2. Создайте проект библиотеки классов для создания кода. Назовите его PetRestApiClient. Используйте .NET Standard 2.1.

  3. В проекте PetReaderToolTaskConsoleApp создайте зависимость от проекта PetRestApiClient.

  4. В проекте PetRestApiClient создайте папку PetRestApiClient. Она будет использоваться для хранения созданного кода.

  5. Удалите файл Class1.cs, который был создан автоматически.

  6. В PetRestApiClient добавьте следующие пакеты NuGet:

    • Newtonsoft.Json, необходимый для компиляции созданного клиента;
    • System.ComponentModel.Annotations, необходимый для компиляции созданного клиента.
  7. В проекте PetRestApiClient создайте текстовый файл с именем petshop-openapi-spec.json (в папке проекта). Чтобы добавить спецификацию OpenApi, скопируйте содержимое отсюда в файл. Мы рекомендуем воспроизводимую сборку, которая зависит только от входных данных, как обсуждалось ранее. В этом примере возникает ошибка сборки, если пользователь выбирает URL-адрес в качестве входных данных спецификации OpenApi.

    Важно!

    Общая перестройка работать не будет. Вы увидите ошибки, указывающие, что не удалось скопировать или удалить RestApiClientGenerator.dll. Это происходит потому, что выполняется попытка создать настраиваемую задачу MSBuild в том же процессе сборки, который ее использует. Выберите PetReaderToolTaskConsoleApp и перестройте только этот проект. Другое решение заключается в размещении настраиваемой задачи в полностью независимом решении Visual Studio, как показано в статье Учебник. Создание настраиваемой задачи.

  8. Скопируйте следующий код в файл 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. Измените инструкции MSBuild, чтобы они вызвали задачу и создали код. Измените PetRestApiClient.csproj, выполнив следующие действия:

    1. Зарегистрируйте использование настраиваемой задачи MSBuild:

      <UsingTask TaskName="RestApiClientGenerator.RestApiClientGenerator" AssemblyFile="..\RestApiClientGenerator\bin\Debug\netstandard2.0\RestApiClientGenerator.dll" />
      
    2. Добавьте свойства, необходимые для выполнения задачи:

       <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>
      

      Важно!

      Выберите правильное значение NSwagCommandFullPath в зависимости от расположения установки в системе.

    3. Добавьте целевой объект MSBuild, чтобы создать клиент во время процесса сборки. Этот целевой объект должен выполняться до выполнением CoreCompile, чтобы создать код, используемый при компиляции.

      <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 и Output связаны с инкрементной сборкой, а целевой объект forceReGenerationOnRebuild удаляет созданный файл после CoreClean, что приводит к повторному созданию клиента во время операции перестройки.

  10. Выберите PetReaderToolTaskConsoleApp и перестройте только этот проект. Теперь будет создан и скомпилирован клиентский код. Вы можете выполнить его и увидеть, как он работает. Этот код создает код из файла, и это разрешено.

  11. На этом шаге вы продемонстрируете проверку параметров. В PetRestApiClient.csprojизмените свойство $(PetClientInputOpenApiSpec), чтобы использовать URL-адрес:

      <PetClientInputOpenApiSpec>https://petstore.swagger.io/v2/swagger.json</PetClientInputOpenApiSpec>
    
  12. Выберите PetReaderToolTaskConsoleApp и перестройте только этот проект. Вы получите ошибку "URL-адрес не разрешен" в соответствии с требованием проектирования.

Загрузка кода

Установите программу командной строки NSwag. Вам понадобится полный путь к каталогу, в котором находится NSwag.exe. После этого измените PetRestApiClient.csproj и выберите правильное значение $(NSwagCommandFullPath) в зависимости от пути установки на компьютере. Теперь выберите RestApiClientGenerator и выполните сборку только этого проекта, а затем выберите и перестройте PetReaderToolTaskConsoleApp. Вы можете выполнить PetReaderToolTaskConsoleApp, чтобы убедиться, что все функции работают.

Следующие шаги

Возможно, вы захотите опубликовать настраиваемую задачу как пакет NuGet.

Кроме того, вы можете узнать, как проверить настраиваемую задачу.