Swashbuckle

前一篇简单介绍如何在一个 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
{
    /// 
    /// Customer API 项目.
    /// 
    public class CustomersController : ApiController
    {
        private Northwind db = new Northwind();
 
        /// 
        /// 取得所有 Customer 数据.
        /// 
        /// IQueryable<Customer>.
        public IQueryable GetCustomers()
        {
            return db.Customers;
        }
 
        /// 
        /// 指定 ID 以取得 Customer 数据.
        /// 
        /// The identifier.
        /// IHttpActionResult.
        [ResponseType(typeof(Customer))]
        public IHttpActionResult GetCustomer(string id)
        {
            Customer customer = db.Customers.Find(id);
            if (customer == null)
            {
                return NotFound();
            }
 
            return Ok(customer);
        }
 
        /// 
        /// 更新 Customer.
        /// 
        /// The identifier.
        /// The customer.
        /// IHttpActionResult.
        [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);
        }
 
 
        /// 
        /// 新增 Customer.
        /// 
        /// The customer.
        /// IHttpActionResult.
        [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);
        }
 
        /// 
        /// 删除 Customer.
        /// 
        /// The identifier.
        /// IHttpActionResult.
        [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;
        }
    }
}

在 Swagger UI 页面上的显示,如下所示:

Model Schema

如果说是 Api 所输出的数据类是自己建立的,那么就需要自己使用 ResponseTypeAttribute 标示在 Action 方法上,如下:

在 Swagger UI 的显示内容

Model Schema

Model

XML 文档注解 - remarks

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

标记可用于加入类型的相关资讯,补充以

指定的资讯。会显示此资讯。

在 XML 文档注解的

标记应用于描述类型或类型成员。而使用 为类型描述加入补充资讯。

直接来看看两个的不同,

在 Swagger UI 的显示

上图“1”所显示的是 summary 内容,而“2”则是显示 remarks 的内容,

把每个 Operation 缩合起来,在列表上就会看 summary 的内容来辨识。所以有需要特别对 Api 做补充描述或是比较长的叙述时,就可以写在 remarks 里,这样在 swagger 里就可以一并输出显示。

XML 文档注解 - response

在 Bruce Chen 的“KingKong Bruce记事: ASP.NET Web API 文档产生器(2) - Swagger”这一篇文章里就有介绍到这一个 XML 文档注解,如果一个 Api 的输出会有其他自订的内容时,可以使用 response,

在 Swagger UI 页面上的输出如下

一个 Api 的输出可能因为不同的状况而输出不同的内容,所以 response 也可以使用多个,

Response 的 StatusCode 为 2xx,那么就会将内容显示在上面的位置,如果是其他的 4xx 或 5xx,则会显示在下面的位置,

不过这一个做法在“Microsoft Azure 文档 - 自订 Swashbuckle 产生的 API 定义”这一篇文章里有特别说到是 Swashbuckle 5.1.5 版之前,这个方法是适用的,那如果是之后的版本呢?

SwaggerResponseAttribute

如果你是现在新安装 Swashbuckle 的话,那么可以看看 packages.config 的内容,应该都已经是 5.2.2 之后的版本,

这边可以使用 SwaggerResponseAttribute 这个类去替代 XML 文档注解 response ,

修改如下

显示结果

与之前使用 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

以上

分享