教學課程:產生 REST API 用戶端

取用 REST API 的應用程式是很常見的案例。 通常,您必須產生應用程式可以用來呼叫 REST API 的用戶端程式碼。 在本教學課程中,您將了解如何使用 MSBuild 在建置過程中自動產生 REST API 用戶端。 您將使用 NSwag,這是為 REST API 產生用戶端程式碼的工具。

完整的範例程式碼可在 GitHub 上 .NET 範例存放庫中的 REST API 用戶端產生取得。

此範例顯示一個取用公用 Pet Store API 的主控台應用程式,其會發佈 OpenAPI 規格

本教學課程假設您具備 MSBuild 術語的基本知識,例如工作、目標、屬性或執行階段;如需必要的背景,請參閱 MSBuild 概念一文

當您想要在建置過程中執行命令列工具時,有兩種方法可以考慮。 其中一種方法是使用 MSBuild Exec 工作,其可讓您執行命令列工具並指定其參數。 另一種方法是建立衍生自 ToolTask 的自訂工作,這讓您擁有更大的控制權。

必要條件

您應該了解 MSBuild 概念,例如工作、目標和屬性。 請參閱 MSBuild 概念

這些範例需要隨 Visual Studio 一起安裝,但也可以單獨安裝的 MSBuild。 請參閱在沒有 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 目標,以在建置過程中產生用戶端。

    首先,新增一些對產生用戶端有用的屬性:

     <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" 新增至 <Exec> 標籤,然後使用 <Output> 標籤中的 ConsoleOutput 參數來擷取輸出。 ConsoleOutput 會以 Item 形式傳回輸出。 空白字元會遭修剪。 當 ConsoleToMSBuild 為 true 時,ConsoleOutput 便會啟用。

    稱為 forceReGenerationOnRebuild 的第二個目標會在清除期間刪除產生的類別,以強制在重建目標執行期間重新產生已產生的程式碼。 此目標會在 CoreClean MSBuild 預先定義的目標之後執行。

  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 問題。 請參閱使用 HttpClientFactory 實作復原 HTTP 要求

恭喜! 現在,您可以執行程式來查看其運作方式。

選項 2:衍生自 ToolTask 的自訂工作

在許多情況下,使用 Exec 工作就足以執行外部工具,來執行 REST API 用戶端程式碼產生之類的動作,但如果您想要當且僅當不使用絕對 Windows 路徑作為輸入時,才允許 REST API 用戶端程式碼產生,該怎麼辦? 或者,如果您需要以某種方式計算可執行檔的所在位置,該怎麼辦? 若有您需要執行一些程式碼來完成額外工作的任何情況時,MSBuild 工具工作是最佳解決方案。 ToolTask 類別是衍生自 MSBuild Task 的抽象類別。 您可以定義具象子類別,其會建立自訂 MSBuild 工作。 此方法可讓您執行準備命令執行所需的任何程式碼。 您應該先閱讀教學課程建立自訂工作進行程式碼產生

您將建立衍生自 MSBuild ToolTask 的自訂工作,其會產生 REST API 用戶端,但如果您嘗試使用 HTTP 位址參考 OpenApi 規格,則其會設計為發出錯誤。 NSwag 支援 HTTP 位址作為 OpenApi 規格輸入,但基於此範例的目的,假設有不允許這樣做的設計需求。

完整程式碼位於此 PetReaderToolTaskExample 資料夾中;您可以下載並查看。 在本教學課程中,您將逐步完成並了解一些您可以套用到自己案例的概念。

  1. 為自訂工作建立新的 Visual Studio 專案。 將其稱為 RestApiClientGenerator,並使用類別庫 (C#) 範本搭配 .NET Standard 2.0。 將方案命名為 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. 有許多您可以覆寫的方法。 針對目前的實作,定義下列兩個:

    • 定義命令參數:
      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'。 這是因為其正嘗試在使用其的同一建置流程中建置 MBuild 自訂工作。 選取 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>
      

    InputOutput累加建置相關,而且 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 套件。

或者,了解如何測試自訂工作。