網頁

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

 

以上

沒有留言:

張貼留言