[WEB API]為團隊訂製一個優秀文件和友善的測試,使用SWAGGER

為團隊訂製一個優秀文件和友善的測試,使用SWAGGER

前言

web api通常開發出來,就代表了可以多個專案使用同樣的邏輯,甚至是排除了任何程式語言,不管你是手機或者javascript或者是ruby etc..,全部都可以使用,所以現在有很多專案都漸漸的翻成了web api,甚至有多微服務的概念也是用web api的角度去思考而架構,但是如果有一天發現不同部門也想用到我們的資料,這時候文件就很重要,然而如果是提供紙本文件的話,我們工程師都知道,文件可能都還需要溝通過,然後了解怎麼使用才開始寫code去try,就會很浪費時間和成本,所以如果我們能提供一份線上文件,直接給團隊去瀏覽和測試,就能節省很多時間成本的浪費,至少如果我們用swagger來寫文件,有比較統一的文件格式,甚至是一些qa或pm都能直接透過線上文件來了解細節或測試,接著我就來說明一下如何使用Swagger吧。

導覽

  1. 安裝swashbuckle
  2. 開始一個簡單的swagger文件吧
  3. 針對傳輸物件或者是回傳物件定義較清楚的說明
  4. 如何在佈署的時候,也自動產生一份XML
  5. 在線上階段,關掉swagger的功能
  6. 結論

 

安裝swashbuckle

首先使用nuget安裝相關必要的package

安裝完之後可以看到多了一支檔案是App_Start/SwaggerConfig.cs的檔案,裡面已經很詳細的註解了每段程式碼的意義了。

 

開始一個簡單的swagger文件吧

首先我們來改一下SwaggerConfig.cs吧

public class SwaggerConfig
    {
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;
            GlobalConfiguration.Configuration 
                .EnableSwagger(c =>
                    {
                        c.SingleApiVersion("v1", "WebApiTest");
                        c.IncludeXmlComments(GetXmlCommentsPath());
                    })
                .EnableSwaggerUi();
        }

        internal static string GetXmlCommentsPath()
        {
            return string.Format(@"{0}\App_Data\XmlDocument.xml", 
                System.AppDomain.CurrentDomain.BaseDirectory); //XmlDocument.xml是建置後產生的檔案,Swagger會參考這份xml
        }
    }

接著對專案選擇屬性

然後把XML documentation file: 的部份打上我們剛剛swagger要輸出的xml

然後把web api都加入summary的部份

/// <summary>
    /// 員工相關
    /// </summary>
    [RoutePrefix("/Api/Values")]
    public class ValuesController : ApiController
    {
        /// <summary>
        /// 取得資料
        /// </summary>
        /// <returns></returns>
        [Route("Get")]
        [HttpGet]
        public IHttpActionResult Get()
        {
            return Ok(EmployeeDao.Employees);
        }
        
        /// <summary>
        /// 用單號取得單筆資料
        /// </summary>
        /// <param name="id">單號</param>
        /// <returns></returns>
        [Route("GetById")]
        [HttpGet]
        public IHttpActionResult GetById(int id)
        {
            return Ok(EmployeeDao.Employees.FirstOrDefault(x => x.EmployeeId == id));
        }

        /// <summary>
        /// 搜尋資料
        /// </summary>
        /// <param name="search">搜尋物件</param>
        /// <returns></returns>
        [Route("Search")]
        [HttpGet]
        public IHttpActionResult Search([FromUri]Search search)
        {
            return Ok(EmployeeDao.Employees.FirstOrDefault(x => x.Name.Contains(search.Name) &&
                    x.Address.Contains(search.Address)));
        }

        /// <summary>
        /// 新增資料
        /// </summary>
        /// <param name="employee">員工</param>
        /// <returns></returns>
        [Route("Post")]
        [HttpPost]
        public IHttpActionResult Post(Employee employee )
        {
            EmployeeDao.Employees.Add(employee);
            return Ok(EmployeeDao.Employees);
        }

        /// <summary>
        /// 修改資料
        /// </summary>
        /// <param name="employee">員工</param>
        /// <returns></returns>
        [Route("Put")]
        [HttpPut]
        public IHttpActionResult Put(Employee employee)
        {
            var result=EmployeeDao.Employees.FirstOrDefault(x => x.EmployeeId == employee.EmployeeId);
            if (result != null)
            {
                result.Name = employee.Name;
                result.PhoneNumber = employee.PhoneNumber;
                result.Address = employee.Address;
            }
            return Ok(EmployeeDao.Employees);
        }

        /// <summary>
        /// 刪除資料
        /// </summary>
        /// <param name="id">單號</param>
        /// <returns></returns>
        [Route("Delete")]
        [HttpDelete]
        public IHttpActionResult Delete(int id)
        {
            EmployeeDao.Employees.RemoveAll(x=>x.EmployeeId==id);
            return Ok(EmployeeDao.Employees);
        }
    }

緊接著只要在我們的網址後面加上路由/swagger,就可以訪問了

 

針對傳輸物件或者是回傳物件定義較清楚的說明

在此以Post來做範例,假設我所有的物件都有加上summary的說明,示例如下

然後我們Post的Api程式碼如下

/// <summary>
/// 新增資料
/// </summary>
/// <param name="employee">員工</param>
/// <returns></returns>
[Route("Post")]
[HttpPost]
public IHttpActionResult Post(Employee employee )
{
    EmployeeDao.Employees.Add(employee);
    return Ok(EmployeeDao.Employees);
}

接著就可以看到swagger的post顯示內容如下

可以看到回傳的部份沒有資料,這個是因為我們使用了IHttpActionResult,導致沒有回傳型別,如果我們使用的是有型別的回傳,那麼就能看到回傳型別了

不過這不符合我們的需求,因為我們需要回應狀態碼給前端,告訴前端伺服器到底遇到了什麼狀況,所以我們必須得使用IHttpActionResult的方式,那我們可以加上ResponseType的attribute

/// <summary>
/// 新增資料
/// </summary>
/// <param name="employee">員工</param>
/// <returns></returns>
[ResponseType(typeof(Employee))]
[Route("Post")]
[HttpPost]
public IHttpActionResult Post(Employee employee )
{
    EmployeeDao.Employees.Add(employee);
    return Ok(EmployeeDao.Employees);
}

結果會像這個樣子

 

如何在佈署的時候,也自動產生一份XML

在預設的狀況下,我們在Debug的時候,只要建置的時候,就會重新產生一XmlDocument.xml在App_Data裡面,但是在實際狀況下此份xml可能並不會簽入版控,甚至如果有搭配ci的話,可能會在例如jenkins甚至team city等等的機器上,自動建置甚至是自動佈署,但是當我們切換去release的狀態,甚至是其他如果我們自己有新增別的configuration的話,可能就沒辦法自動產生xml,就會造成編譯失敗,或者是預期該看到swagger卻沒有看到的狀況,那接下來就來說明一下我們該怎麼設定也能在任何configuration都能自動產生xml出來。

假設我們現在把XmlDocument.xml這個檔案在方案總管給刪除了,然後切換到release做建置的話,其實xml是不會產生出來的,只有在Debug的狀態才會產生xml出來

然後把檔案刪掉,接著切換到release再建置專案,其實xml是不會產生出來的

這時候我們只要打開專案下的csproj,接著搜尋我們的xml名稱,然後把那行複製到release或者是自定義的其他configuration,切換到那個configuration做建置,就會自動產生xml出來了

 

在線上階段,關掉swagger的功能

因為swagger是為了方便理解還有測試和溝通,所以我們才製作此種文檔,但是我們不可能會把這種功能放到production上面,那我們又該如何在上線階段去關閉呢?其實也很簡單,比如說我只想在Debug或者Qa的環境,可以去使用swagger功能,我們只要在App_Start/SwaggerConfig.cs,修改切換環境的方式就可以了。

public class SwaggerConfig
    {
        public static void Register()
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;
            GlobalConfiguration.Configuration
                .EnableSwagger(c =>
                    {
                        c.SingleApiVersion("v1", "WebApiTest");
                        c.IncludeXmlComments(GetXmlCommentsPath());
#if DEBUG || QA
                    })
                .EnableSwaggerUi();
#else
                    });
#endif

                    }

        internal static string GetXmlCommentsPath()
        {
            return string.Format(@"{0}\App_Data\XmlDocument.xml", 
                System.AppDomain.CurrentDomain.BaseDirectory); //XmlDocument.xml是建置後產生的檔案,Swagger會參考這份xml
        }
    }

 

結論

swagger還有蠻多的內容,有興趣的讀者可以自行再去研究一下,如果有任何建議的話,再請不吝指教