評估過 AutoRest、NSwag、Swagger CodeGen 的客戶端程式碼產生功能,我選擇 Swag Studio,理由是 GUI 介面友善,不依賴第三方套件,且輸出結果為單一檔案,較符合我的要求。

實務應用上,難免需要微調產出結果以符合專案的特殊要求,而我遇到的情境得支援 .NET 3.5,更是不改不行,故如何客製客戶端程式範本為首要之急,阻礙太大時甚至該當機立斷早早轉向。

經過一番研究,做到修改範本支援 .NET 3.5 版的目標,過程有點繁瑣,特整理分享給有類似需求的同學參考。

先說我的改法:在 NSwag Studio 上加一個「For .NET 3.5」選項,選取後改輸出適用 .NET 3.5 的程式碼。

.NET 3.5 版與原有版本主要差別在 .NET 3.5 沒有 System.Threading.Tasks(4.0+) 及 HttpClient(4.5+),不支援 async、await,還有一些零星小差別。一開始本想靠替代套件(System.Threading.Tasks.UnofficialRackspace.HttpClient35)解決,但替代程式庫與 .NET 內建版本存在差距,程式終究得改。何況依賴外部程式庫,有違選擇 NSwag Studio 的初衷,故我採行的策略是針對差異較大的部分另寫一套 .NET 3.5 樣版,細節容後再提。

修改後的 NSwag Studio 長這樣。理論上,勾選 For .NET 3.5 後,有些選項不適用該停用或隱藏(如 Inject HttpClient via constructor... 等等),但自用工具就不鈑烤打腊了,達到目的比較重要。

下面是修改細節。首先,我從 Github Fork 專案取得 NSwag 原始碼。 開啟 src/NSwag.sln,這個完整版解決方案共有 45 個專案,從核心元件、Console 程式、ASP.NET Core Middleware、自動測試、NuGet 程式包、Chocolatey 程式包到 NSwag Studio,應有盡有。

產生 C# 的樣版在 NSwag.CodeGeneration.CSharp 專案 Templates 目錄下,裡面有一堆 .liquid 附檔名的檔案,

.liquid 是 Ruby 的 Liquid Markup 樣版,用來依據 Model 物件產生 C# 程式, Liquid 語法類以這樣:

大家若有 ASP 或 cshtml 經驗,要看懂及改寫不算難。改寫幅度太大的部分,我選擇拆檔(例如上上圖中的 Client.Class.liquid 與 Client.Class35.liquid)以簡化樣版結構,由於原本的設計已模組化,有多處靠 {% template Client.Class %} 方式載入外部樣版,故可在源頭加入 if 邏輯搞定:

{%     if GenerateImplementation -%}
{%          if Net35Mode -%}
{% template Client.Class35 %}
{%          else -%}
{% template Client.Class %}
{%          endif -%}
{%     endif -%}

再來是加入 Net35Mode 參數,共有幾個地方:

  • NSwag.CodeGeneration.CSharp\SwaggerToCSharpClientGeneratorSettings.cs
    加入 Net35Mode 屬性
  • NSwag.CodeGeneration.CSharp\Models\CSharpClientTemplateModel.cs
    這是要傳入 Liquid 樣版的 Model,需新增 Net35Mode 傳出 SwaggerToCSharpClientGeneratorSettings.Net35Mode
  • NSwag.Commands\Commands\CodeGeneration\SwaggerToCSharpClientCommand.cs
    這是 NSwag Studio WPF 用的 ViewModel,也要加入 Net35Mode 屬性對應 SwaggerToCSharpClientGeneratorSettings.Net35Mode
  • NSwagStudio\Views\CodeGenerators\SwaggerToCSharpClientGeneratorView.xaml
    在 UI 加上 Checkbox 繫結到 SwaggerToCSharpClientCommand.Net35Mode 屬性

修改後要部署 NSwag Studio 測試稍麻煩,建議可在測試專案增加測試項目,如心有餘力將測試案例補齊就更完美了。

我先在 NSwag.CodeGeneration.CSharp.Tests\SCharpClientSettingsTests.cs 加入以下方法,測試啟用 Net35Mode 時程式碼不會出現 Task,基本上就是成功了。

[Fact]
public async Task When_net35Mode_is_set_then_no_Task_is_found()
{
    //// Arrange
    var swaggerGenerator = new WebApiToSwaggerGenerator(
        new WebApiToSwaggerGeneratorSettings());

    //為假想對象 ApiController 建立 Swagger 文件
    var document = await swaggerGenerator.GenerateForControllerAsync<FooController>();

    //指定不同參數
    var generator = new SwaggerToCSharpClientGenerator(document,
        new SwaggerToCSharpClientGeneratorSettings
        {
            Net35Mode = true
        });

    //// Act
    var code = generator.GenerateFile();

    //// Assert
    Assert.DoesNotContain("Task", code);
}

最後說一下如何執行修改版 NSwag Studio。

NSwag Studio 產生 C# 程式碼時並不是直接呼叫元件,而是在 Temp 目錄產生一個 nswag_document_75debafd-8a81-41b5-93c6-007cb39cf461_config.json 設定檔,再呼叫指定 Runtime 目錄下的 NSwag.exe(Win) / NSwag.dll(.NET Core) 命令列工具程式產生程式碼。設計成這樣是為了讓 AspNetCoreToSwaggerCommand、WebApiToSwaggerCommand、TypesToSwaggerCommand 可以在獨立 AppDomain (.NET Framework) 或 AssemblyLoadContext (.NET Core) 執行,使其與 ASP.NET 網站或 NSwag Studio 脫鉤(二者使用的平台或 DLL 版本可能不同),詳細說明可參考官方文件:NSwag Assembly loading

故要修改邏輯有多處元件要更新,netcoreapp1.0/1.1/2.0/2.1/2.2 等資料夾位於 \NSwag.ConsoleCore\bin\Release,Win 資料夾則來自 \NSwag.Console\bin\Release\net461 (再加上 \NSwag.Console.x86\bin\Release\net461\ NSwag.x86.exe & NSwag.x86.exe.config ),NSwag Studio 主程式則位於 \NSwagStudio\bin\Release (若有修改 UI 增加選項,NSwag.Commands.dll 與 NSwagStudio.exe)。

經過這番手腳,NSwag Studio 就能產生為專案量身訂做的 C# 程式碼囉~

Tutorial of how to customize the C# code template of NSwag Studio.


Comments

Be the first to post a comment

Post a comment