チュートリアル: REST API クライアントを生成する

アプリケーションで REST API を使うことは、非常に一般的なシナリオです。 通常、アプリケーションが REST API の呼び出しに使うクライアント コードを生成する必要があります。 このチュートリアルでは、MSBuild を使って、ビルド プロセス中に REST API クライアントを自動的に生成する方法を説明します。 NSwag という REST API のクライアント コードを生成するツールを使います。

完全なサンプル コードは、GitHub の .NET サンプル リポジトリの「Rest Api Client Generation (REST API クライアントの生成)」で公開されています。

この例は、OpenAPI 仕様を公開している、パブリックの Pet Store API を使うコンソール アプリを示しています。

このチュートリアルは、タスク、ターゲット、プロパティ、ランタイムなどの MSBuild 用語の基本的な知識を前提としています。必要な背景については、MSBuild の概念に関する記事を参照してください。

ビルドの一部としてコマンドライン ツールを実行する場合、検討すべき 2 つの方法があります。 1 つは、MSBuild の Exec タスクを使う方法で、コマンドライン ツールを実行し、そのパラメーターを指定します。 もう 1 つは、ToolTask から派生したカスタム タスクを作成する方法で、こちらはより優れた制御が可能になります。

前提条件

タスク、ターゲット、プロパティなどの MSBuild の概念について理解している必要があります。 「MSBuild の概念」を参照してください。

この例では、MSBuild が必要です。これは Visual Studio と一緒にインストールされますが、個別にインストールすることもできます。 Visual Studio なしで MSBuild をダウンロードするに関するページを参照してください。

オプション 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 targets を追加して、ビルド処理中にクライアントを生成するようにします。

    まず、クライアント生成に便利なプロパティをいくつか追加します。

     <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 では、入力ファイルのタイムスタンプと出力ファイルのタイムスタンプを比較して、ターゲットをスキップするか、ビルドするか、または部分的にリビルドするかどうかを判断できます。

    NSwag.MSBuild をインストールした後、プロジェクトの NuGet パッケージでは、.csproj ファイル内で変数 $(NSwagExe) を使って、MSBuild ターゲットで NSwag コマンドライン ツールを実行できます。 こうすることで、NuGet を介して簡単にツールを更新できます。 ここでは、Exec MSBuild タスクを使って NSwag プログラムを必要なパラメーターで実行し、クライアントの Rest Api を生成します。 NSwag コマンドとパラメーターに関するページを参照してください。

    <Exec> タグに ConsoleToMsBuild="true" を追加し、<Output> タグの ConsoleOutput パラメーターを使って、<Exec> から出力をキャプチャすることができます。 ConsoleOutput は出力を Item として返します。 空白は切り捨てられます。 ConsoleOutputConsoleToMSBuild が true のときに有効になります。

    forceReGenerationOnRebuild という名前の 2 番目のターゲットは、クリーンアップ中に生成されたクラスを削除して、リビルド ターゲットの実行中に生成されたコードを強制的に再生成します。 このターゲットは CoreClean MSBuild 定義済みターゲットの後に実行されます。

  8. Visual Studio Solution のリビルドを実行し、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}");
           }
       }
    }
    

    Note

    このコードで new HttpClient() を使っているのは、デモンストレーションが簡単だからですが、実際のコードのベスト プラクティスではありません。 ベスト プラクティスは、HttpClientFactory を使って、リソースの枯渇や古くなった DNS の問題など、HttpClient 要求の既知の問題に対処した HttpClient オブジェクトを作成することです。 「IHttpClientFactory を使用して回復力の高い HTTP 要求を実装する」を参照してください。

お疲れさまでした。 これで、プログラムを実行して、その動作を確認できます。

オプション 2: ToolTask から派生したカスタム タスク

多くの場合、REST API クライアント コード生成のような外部ツールを実行するには Exec タスクを使うと十分ですが、Windows の絶対パスを入力として使わない場合にのみ、REST API クライアント コード生成を許可したい場合はどうしたらよいでしょうか。 または、実行可能ファイルがどこにあるかを何らかの方法で計算する必要がある場合はどうでしょうか。 何か特別な作業をするためにコードを実行する必要がある場合、MSBuild Tool Task が最適な解決策になります。 ToolTask クラスは、MSBuild Task から派生した抽象クラスです。 カスタム MSBuild タスクを作成する具象サブクラスを定義できます。 この方法では、コマンド実行の準備に必要なコードを実行することができます。 最初に、チュートリアル「コード生成用のカスタム タスクを作成する」をお読みください。

MSBuild ToolTask から派生したカスタム タスクを作成し、REST API クライアントを生成しますが、http アドレスを使って OpenApi 仕様を参照しようとするとエラーが発生するように設計されます。 NSwag は OpenApi 仕様の入力として http アドレスをサポートしていますが、この例では、それを許可しない設計要件があると仮定します。

完全なコードは、この PetReaderToolTaskExample フォルダーにあります。ダウンロードして確認できます。 このチュートリアルでは、ステップ バイ ステップで、ご自身のシナリオに適用できるいくつかの概念を学習します。

  1. カスタム タスク用の新しい Visual Studio プロジェクトを作成します。 それを RestApiClientGenerator という名前にして、.NET Standard 2.0 の Class Library (C#) テンプレートを使います。 ソリューション PetReaderToolTaskExampleの名前を指定します。

  2. 自動的に生成された Class1.cs を削除します。

  3. Microsoft.Build.Utilities.Core NuGet パッケージを追加します。

    • RestApiClientGenerator というクラスを作成します

    • 次のコードに示すように、MSBuild ToolTask から継承して、抽象メソッドを実装します。

      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. オーバーライドできるメソッドは多数あります。 今回の実装では、この 2 つを定義します。

    • コマンド パラメーターの定義。
      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;
    }
    

    Note

    この単純な検証は、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 仕様を追加するには、ここにある内容をファイルにコピーします。 前に説明したように、入力にのみ依存する再現可能なビルドになるようにします。 この例では、ユーザーが OpenApi 仕様の入力として URL を選んだ場合、ビルド エラーが発生します。

    重要

    通常のリビルドではうまくいきません。 RestApiClientGenerator.dll をコピーまたは削除できないことを示すエラーが表示されます。 これは、MBuild カスタム タスクをビルドしようとしているのと同じビルド プロセスで、そのタスクを使っているためです。 PetReaderToolTaskConsoleApp を選び、そのプロジェクトのみをリビルドしてください。 もう 1 つの解決策は、「チュートリアル: カスタム タスクを作成する」の例で行ったように、カスタム タスクを完全に独立した 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>
      

    InputOutputインクリメンタル ビルドに関連しており、forceReGenerationOnRebuild ターゲットは CoreClean の後に生成されたファイルを削除し、リビルド操作中にクライアントが強制的に再生成されます。

  10. PetReaderToolTaskConsoleApp を選び、そのプロジェクトのみをリビルドしてください。 これで、クライアント コードが生成され、コードがコンパイルされます。 実行して、その動作を確認することができます。 このコードでは、ファイルからコードを生成していますが、これは許可されています。

  11. この手順では、パラメーターの検証を示します。 PetRestApiClient.csproj で、URL を使うようにプロパティ $(PetClientInputOpenApiSpec) を変更します。

      <PetClientInputOpenApiSpec>https://petstore.swagger.io/v2/swagger.json</PetClientInputOpenApiSpec>
    
  12. PetReaderToolTaskConsoleApp を選び、そのプロジェクトのみをリビルドしてください。 設計要件に従って、"URL は許可されていません" というエラーが表示されます。

コードのダウンロード

NSwag コマンドライン ツールをインストールします。 次に、NSwag.exe があるディレクトリへの完全なパスが必要になります。 その後、PetRestApiClient.csproj を編集し、コンピューター上のインストール パスに基づいて適切な $(NSwagCommandFullPath) 値を選びます。 ここで、RestApiClientGenerator を選んでそのプロジェクトのみを構築し、最後に PetReaderToolTaskConsoleApp を選んでリビルドします。 PetReaderToolTaskConsoleApp を実行して、 すべてが期待どおりに動作することを確認します。

次のステップ

カスタム タスクを NuGet パッケージとして公開することができます。

あるいは、カスタム タスクのテスト方法について説明します。