2015年12月29日 星期二

Swashbuckle - Swagger for Web Api 顯示內容的調整

前一篇簡單介紹如何在一個 ASP.NET Web Api 專案裡安裝 Swashbuckle - Swagger for Web Api 這個套件,基本上都沒有什麼難度,特別要注意的就是 Controller 與 Action 方法與相關類別的 XML 文件註解要記得寫以及維護,在一般的使用情況下的 Api 服務資訊提供已經相當清楚了。

不過提供更加清楚的資訊給使用 Api 服務的開發者對於產品的開發與溝通是有絕對幫助的,但老實說,要把 Swagger 寫得好又要能夠搭配 Web Api 後端程式讓資訊可以自動產生,其實也不是容易的事情,這邊就來說明幾個調整顯示資訊的做法。

 


ResponseTypeAttribute

MSDN - ResponseTypeAttribute 類別 (System.Web.Http.Description)

https://msdn.microsoft.com/zh-tw/library/system.web.http.description.responsetypeattribute%28v=vs.118%29.aspx?f=255&MSPPError=-2147217396

使用此項目來指定動作傳回的實體類型 (宣告的傳回類型為 HttpResponseMessage 或 IHttpActionResult)。 ResponseType 會由 ApiExplorer 在產生 ApiDescription 時讀取。

 

如果你是跟著「ASP.NET Web Api - Help Page」這一篇內容然後建立一個新的 Web Api 專案,然後也是用 Entity Framewok 以及使用 Scaffold 去建立 Controller 類別與內容,那麼可以看看其實在每個 Controller 內幾乎所有 Action 方法上都會有使用「ResponseType」這個 Attribute 吧,

例如以下這個 CustomersController.cs

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.Http.Description;
using HelpPageDemo.Models;
 
namespace HelpPageDemo.Controllers
{
    /// <summary>
    /// Customer API 項目.
    /// </summary>
    public class CustomersController : ApiController
    {
        private Northwind db = new Northwind();
 
        /// <summary>
        /// 取得所有 Customer 資料.
        /// </summary>
        /// <returns>IQueryable&lt;Customer&gt;.</returns>
        public IQueryable<Customer> GetCustomers()
        {
            return db.Customers;
        }
 
        /// <summary>
        /// 指定 ID 以取得 Customer 資料.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>IHttpActionResult.</returns>
        [ResponseType(typeof(Customer))]
        public IHttpActionResult GetCustomer(string id)
        {
            Customer customer = db.Customers.Find(id);
            if (customer == null)
            {
                return NotFound();
            }
 
            return Ok(customer);
        }
 
        /// <summary>
        /// 更新 Customer.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <param name="customer">The customer.</param>
        /// <returns>IHttpActionResult.</returns>
        [ResponseType(typeof(void))]
        public IHttpActionResult PutCustomer(string id, Customer customer)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
 
            if (id != customer.CustomerID)
            {
                return BadRequest();
            }
 
            db.Entry(customer).State = EntityState.Modified;
 
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!CustomerExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
 
            return StatusCode(HttpStatusCode.NoContent);
        }
 
 
        /// <summary>
        /// 新增 Customer.
        /// </summary>
        /// <param name="customer">The customer.</param>
        /// <returns>IHttpActionResult.</returns>
        [ResponseType(typeof(Customer))]
        public IHttpActionResult PostCustomer(Customer customer)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
 
            db.Customers.Add(customer);
 
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateException)
            {
                if (CustomerExists(customer.CustomerID))
                {
                    return Conflict();
                }
                else
                {
                    throw;
                }
            }
 
            return CreatedAtRoute("DefaultApi", new { id = customer.CustomerID }, customer);
        }
 
        /// <summary>
        /// 刪除 Customer.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>IHttpActionResult.</returns>
        [ResponseType(typeof(Customer))]
        public IHttpActionResult DeleteCustomer(string id)
        {
            Customer customer = db.Customers.Find(id);
            if (customer == null)
            {
                return NotFound();
            }
 
            db.Customers.Remove(customer);
            db.SaveChanges();
 
            return Ok(customer);
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
 
        private bool CustomerExists(string id)
        {
            return db.Customers.Count(e => e.CustomerID == id) > 0;
        }
    }
}

image

在 Swagger UI 頁面上的顯示,如下所示:

Model Schema

image

 

如果說是 Api 所輸出的資料類別是自己建立的,那麼就需要自己使用 ResponseTypeAttribute 標示在 Action 方法上,如下:

image

在 Swagger UI 的顯示內容

Model Schema

image

Model

image

 

XML 文件註解 - remarks

https://msdn.microsoft.com/zh-tw/library/3zw4z1ys(v=vs.140).aspx

<remarks> 標記可用於加入型別的相關資訊,補充以 <summary> 指定的資訊。會顯示此資訊。

在 XML 文件註解的 <summary> 標記應用於描述型別或型別成員。而使用 <remarks> 為型別描述加入補充資訊。

直接來看看兩個的不同,

image

在 Swagger UI 的顯示

image

上圖「1」所顯示的是 summary 內容,而「2」則是顯示 remarks 的內容,

image

把每個 Operation 縮合起來,在列表上就會看 summary 的內容來辨識。所以有需要特別對 Api 做補充描述或是比較長的敘述時,就可以寫在 remarks 裡,這樣在 swagger 裡就可以一併輸出顯示。

 

XML 文件註解 - response

在 Bruce Chen 的「KingKong Bruce記事: ASP.NET Web API 文件產生器(2) - Swagger」這一篇文章裡就有介紹到這一個 XML 文件註解,如果一個 Api 的輸出會有其他自訂的內容時,可以使用 response,

image

在 Swagger UI 頁面上的輸出如下

image

一個 Api 的輸出可能因為不同的狀況而輸出不同的內容,所以 response 也可以使用多個,

image

Response 的 StatusCode 為 2xx,那麼就會將內容顯示在上面的位置,如果是其他的 4xx 或 5xx,則會顯示在下面的位置,

image

不過這一個做法在「Microsoft Azure 文件 - 自訂 Swashbuckle 產生的 API 定義」這一篇文章裡有特別說到是 Swashbuckle 5.1.5 版之前,這個方法是適用的,那如果是之後的版本呢?

 

SwaggerResponseAttribute

如果你是現在新安裝 Swashbuckle 的話,那麼可以看看 packages.config 的內容,應該都已經是 5.2.2 之後的版本,

image

這邊可以使用 SwaggerResponseAttribute 這個類別去替代 XML 文件註解 response ,

image

修改如下

image

顯示結果

image

與之前使用 XML 文件註解 response 不同的是,HttpStatusCode 204 的內容並不會顯示在上面 Response Class 的地方,而是顯示在下方的 Response Messages 的區塊,以 Swagger 來說,HttpStatusCode 為 200 的輸出是正常的執行結果而且是唯一有效的回應。

 


深入瞭解之後才發覺到 Swagger 及 Swashbuckle - Swagger for Web API 有好多設定,蠻多資訊都還需要在網路上去四處蒐羅,以後如果再有發現到什麼不一樣以及有幫助的的設定,屆時會再分享給大家。

 

參考連結

KingKong Bruce記事: ASP.NET Web API 文件產生器(2) - Swagger

Microsoft Azure 文件 - 自訂 Swashbuckle 產生的 API 定義

https://github.com/domaindrivendev/Swashbuckle

Swagger and ASP.NET Web API - Part I: Adding Swagger to Web API project

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。