[Swagger] 一些 Swagger 編寫文件的技巧和 Client Code Gen

前面幾篇寫了使用 Swagger 的方式,這篇記錄一下編寫文件的技巧以及支援 Client Code Gen 幾種方式

開發環境

  • VS 2017 Enterprise 15.9.5
  • Swashbuckle 5.6.0

本文連結 


前置作業

安裝

  • Install-Package Swashbuckle
  • Install-Package Swagger-Net
  • 瀏覽 /swagger

設定

@RouteConfig.cs

設定 Swagger 為預設頁面,非必要

httpConfig.Routes.MapHttpRoute("swagger_root",
				"",
				null,
				null,
				new RedirectHandler(message => message.RequestUri.ToString(),
						"swagger"));

@SwaggerConfig.cs

找到 IncludeXmlComments 然後解開註解

c.IncludeXmlComments(GetXmlCommentsPath());

增加以下方法

private static string GetXmlCommentsPath() { return Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"App_Data\Server.XML"); }

 

輸出文檔,別忘了這裡的路徑要跟上面步驟一樣

 

編寫文件

註解

詳細作法請參考以下連結
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/recommended-tags-for-documentation-comments

 

實驗了一下, Swagger 支援這幾個 tag,其中比較特別的是 response code

/// <summary> 
///     新增會員
/// </summary>
/// <param name="member">會員參數</param> 
/// <response code="200">OK</response> 
/// <response code="400">Not found</response> 
/// <returns></returns> 
/// <remarks>注意事項</remarks> 
public IHttpActionResult Post(Member member)
{
	return this.Ok();
}

 

轉成 Swagger 後,如下圖

 

Model 定義

/// <summary>
///     會員
/// </summary>
public class Member
{
	/// <summary>
	///     編碼
	/// </summary>
	[Required]
	public Guid Id { get; set; }

	/// <summary>
	///     姓名
	/// </summary>
	[StringLength(100)]
	public string Name { get; set; }

	/// <summary>
	///     郵件
	/// </summary>
	[EmailAddress]
	public string Email { get; set; }

	/// <summary>
	///     年齡
	/// </summary>
	[Range(1, 100)]
	public int Age { get; set; }
}

 

Swagger 會把綁定的 Model 變成,Model、Example Value 兩個區段。

它支援 [Required]、[StringLength]、[Range(1, 100)],可能還有支援更多的 Attribute,可以自己試試看。

某些 Attribute 需要透過滑鼠事件才會呈現,例如:[StringLength]。

 

ResponseType

/// <summary> 
///     新增會員
/// </summary> 
/// <param name="member">會員參數</param> 
/// <returns></returns> /// <remarks>注意事項</remarks> [ResponseType(typeof(Member))] 
public IHttpActionResult Post(Member member)
{
return this.Ok();
}

 

回傳型別

 

SwaggerResponse

ResponseType 稍嫌不足,SwaggerResponse 可填寫更多的內容

/// <summary> 
///     新增會員 
/// </summary>
/// <param name="member">會員參數</param>
/// <returns></returns> 
/// <remarks>注意事項</remarks>
[SwaggerResponse(HttpStatusCode.OK,                  "成功",    typeof(Member))]
[SwaggerResponse(HttpStatusCode.NoContent,           "找不到資料", typeof(void))]
[SwaggerResponse(HttpStatusCode.InternalServerError, "嚴重錯誤")]
public IHttpActionResult Post(Member member)
{
	return this.Ok();
}

 

除了在 Action 可以用之外,也可以用在 Controller,把重複的 Status Code 集中起來

/// <summary> 
/// 會員管理作業
/// </summary>
[SwaggerResponse(HttpStatusCode.OK,                  "成功",    typeof(Member))]
[SwaggerResponse(HttpStatusCode.NoContent,           "找不到資料", typeof(void))]
[SwaggerResponse(HttpStatusCode.InternalServerError, "嚴重錯誤")]
public class ValueController : ApiController
{
}

 

或是設計一個 BaseApiController,讓 Api 使用

/// <summary> 
/// 會員管理作業 
/// </summary> 
[SwaggerResponse(HttpStatusCode.OK,                  "成功",    typeof(Member))]
[SwaggerResponse(HttpStatusCode.NoContent,           "找不到資料", typeof(void))]
[SwaggerResponse(HttpStatusCode.InternalServerError, "嚴重錯誤")]
public class BaseApiController : ApiController
{
}

 

運行結果,Response 就有 Status Code 以及 Model

Controller 跟 Action 衝突時,以 Action 為主

 

SwaggerOperation

操作分類,將一個 Action 分成多個類別

/// <summary> 
///     新增會員
/// </summary>
/// <param name="member">會員參數</param> 
/// <returns></returns>
/// <remarks>注意事項</remarks>
[SwaggerOperation(Tags = new[] {"會員管理作業", "MemberFlow"})]
public IHttpActionResult Post(Member member)
{
	return this.Ok();
}

 

結果如下圖
 

 

Swashbuckle.Examples

只有 Model 還不夠詳細,Swashbuckle.Examples 還可以將 model 要填寫的欄位變成範例,可以更清楚的描述 in/out。

安裝
Install-Package Swashbuckle.Examples

Install-Package Swagger-Net.Examples

SwaggerRequestExample / SwaggerResponseExample

實作 IExamplesProvider,把範例放到 GetExamples 裡面

class MemberExamplesProvider : IExamplesProvider
{
	public object GetExamples()
	{
		return new Member {Id = Guid.NewGuid(), Name = "yao", Age = 19};
	}
}

 

掛在 Action

/// <summary>
///     新增會員 
/// </summary>
/// <param name="member">會員參數</param>
/// <returns></returns> 
/// <remarks>注意事項</remarks> 
[SwaggerRequestExample(typeof(Member), typeof(MemberExamplesProvider))]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(MemberExamplesProvider))]
public IHttpActionResult Post(Member member)
{
	return this.Ok();
}

 

@SwaggerConfig.cs

套用

c.OperationFilter<ExamplesOperationFilter>();

 

運行,Request / Response 換成了我們想要的資料內容了,這樣一來能讓調用端清楚的知道 in/out 的變化,再來調試也變方便了


 

我不會這招來當作主要的開發技巧,我是用 [Web API] 使用 OWIN 進行單元測試 + Specflow

 

SwaggerResponseHeader

這個 Attribute 也能為 Header 加上說明

/// <summary> 
///     新增會員 
/// </summary> 
/// <param name="member">會員參數</param>
/// <returns></returns> /// <remarks>注意事項</remarks> 
[SwaggerResponseHeader(HttpStatusCode.OK, "result", "string", "結果")]
public IHttpActionResult Post(Member member)
{
	return this.Ok();
}

 

@SwaggerConfig.cs

套用

c.OperationFilter<ExamplesOperationFilter>();

 

運行結果如下


 

DefaultValueAttribute

DefaultValueAttribute 也可以產生出跟上述 Example 一樣的效果,例如: [DefaultValue("123456")]
 

SwaggerHub

當你不想直接給用戶使用你的 Swagger UI 時,可以考慮把他放到 swaggerhub:https://swagger.io/tools/swaggerhub/


 

 

登入之後,就能把 json 或 api 匯入,由於我的 api url 沒有對外,所以我用 json 演練

Swagger UI 裡面就有完整的 json

 

把他另存

 

匯入 json

 

公開 API 說明,讓別人也可以瀏覽

 

Code Gen

Web API 並沒有像 Web Service 專案有 wsdl 可以讓 VS IDE 幫我們產生 Proxy Client 程式碼,Web API 可以借助 Swagger.json 來產生 Client 程式碼,下面幾套工具都是利用這樣的方式完成的

 

Swagger Code Gen

Swagger Hub 除了匯入之外,也能匯出 Client

透過腳本 Powershell

  1. Invoke-WebRequest -OutFile swagger-codegen-cli.jar http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.3.1/swagger-codegen-cli-2.3.1.jar
  2. java -jar .\\swagger-codegen-cli.jar generate -i http://localhost:55691/swagger/docs/v1 -l csharp -o d:\client

直接呼叫 swagger-codegen-cli

swagger-codegen-cli.jar 下載位置:
https://github.com/swagger-api/swagger-codegen#prerequisites
 

範例:

java -jar swagger-codegen-cli-3.0.25.jar generate -i http://localhost:55691/swagger/docs/v1 -l csharp -c "swagger.config.json" -o "client"

swagger.config.json 是組態設定,通過 java -jar swagger-codegen-cli-3.0.25.jar config-help -l csharp 取得相關命令,範例如下:

{
    "packageName": "THS.ActiveDirectory.Management.Client",
    "targetFramework": "v4.5",
    "netCoreProjectFile": "true"
}

 

參考 https://github.com/swagger-api/swagger-codegen

 

AutoRest Code Gen

https://github.com/Azure/autorest

這是微軟開發的 OpenAPI (f.k.a Swagger) Client 程式產生器,支援 C#, PowerShell, Go, Java, Node.js, TypeScript, Python, Ruby and PHP.

使用方式相當簡單,先從 npm 安裝 autorest,再把 swagger.json 轉成 C#

npm install -g autorest

轉換

autorest --input-file=http://localhost:55691/swagger/docs/v1 --output-folder=e:\APIClient --csharp

autorest --input-file=e:\APIClient\V1.js --output-folder=e:\APIClient --csharp

更多參數請參考:https://github.com/Azure/autorest/blob/master/docs/user/command-line-interface.md

 

NSwagStudio

https://github.com/RSuter/NSwag/wiki/NSwagStudio

兩種安裝方式

MSI installer: Download latest NSwagStudio MSI installer (Windows Desktop application)

Chocolatey package: NSwagStudio

choco install nswagstudio

 

安裝完之後就會有一個UI,把 Swagger.json 丟進去產生 C# 的 Client 應用程式

這套工具除了支援 Swagger.json 也支援 .NET Assembly,功能蠻強大,有機會再深入把玩

https://github.com/RSuter/NSwag
 

轉換心得

使用轉換工具的目地,是希望可以幫我把 Request 和 Reponse 所對應的型別轉換成 Client 代碼,以下列出我使用的結果

Server 我使用 Swagger-NET ,定義一個 Action,代碼如下:

[HttpGet]
[Route("{domainName}/{userId}/employeeid")]
[SwaggerResponse(HttpStatusCode.OK, "成功", typeof(string), "string", "text/plain; charset = UTF-8", "A001")]
[SwaggerResponse(HttpStatusCode.NoContent, "找不到資料")]
public async Task<IHttpActionResult> GetEmployeeId(string            domainName,
                                                   string            userId,
                                                   CancellationToken cancel = default)
{
    ...
    return OK();
}

 

接下來,使用 Swagger Codegen 、AutoRest、NSwag 工具轉換看看會有甚麼結果​

Swagger Codegen

  • 產生完整方案。
  • 依賴 RestSharp、JsonStubTypes、Newtonsoft.Json。
  • 可支援 .NET 3.5。
  • 當指定多個 SwaggerResponse,產生出來的 Code:
    • 不判斷 HttpStatusCode,只會去讀取 Content,再根據 Response 的型別決定要不要做反序列化。
    • Error Hanlder 判斷 HttpStatusCode 大於 400 及 等於 0,拋出例外。
  • 產生出來的列舉型別數值跟 json 不一致,比如 json 檔的列舉值 None=0,轉換後變成 None=1

AutoRest

  • 產生為多個檔案。
  • 依賴 Microsoft.Rest.ClientRuntime、Newtonsoft.Json from nuget
  • 需 .NET 4.5 以上。
  • 當指定多個 SwaggerResponse / ProducesResponseType,產生出來的 Code
    • 會判斷 HttpStatusCode,不在範圍內的則拋出例外
    • 不管 Response 的型別是甚麼都會反序列化

NSwag

  • 產生結果為單一 .cs 檔,提供 GUI 操作介面友,
  • 依賴 Newtonsoft.Json
  • .NET 4.5 以上。
  • 當指定多個 SwaggerResponse / ProducesResponseType,產生出來的 Code
    • 會判斷 HttpStatusCode,不在範圍內的則拋出例外
    • 不管 Response 的型別是甚麼都會反序列

下圖以 NoContent 204 為例

 

結論

  1. Request:Action 的參數綁定簡單型別跟複雜型別都可以轉換
  2. Response:可以使用 SwaggerResponse、ResponseType Attribute 明確指定型別,工具都會幫我們產生正確的型別,但是每一套的處理機制都不一樣

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo