天天看點

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔

建立Web API工程

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔

選Empty,勾選Web API,不要選擇Web API,那樣會把MVC勾上,這裡不需要MVC

Web API工程屬性

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔

 XML檔案用于生成線上文檔

  建立Windows服務作為Web API的宿主

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
WebApiHost工程屬性
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔

 控制台應用程式友善調試

 Windows服務安裝Microsoft.AspNet.WebApi.OwinSelfHost

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔

工程WebApiDemo需要引用Microsoft.Owin.dll

 WebApiDemo安裝Swashbuckle

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
 應用程式入口
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;

namespace WebApiHost
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主入口點。
        /// </summary>
        static void Main(string[] args)
        {
            RunDebug();
            StartService();
        }

        private static void StartService()
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new WebApiHostService()
            };
            ServiceBase.Run(ServicesToRun);
        }

        [Conditional("DEBUG")]
        private static void RunDebug()
        {
            new WebApiHostService().Start();
            Console.WriteLine("啟動成功");
            Console.ReadLine();
        }
    }
}      

View Code

 啟動Web API服務

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Microsoft.Owin.Hosting;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Utils;

namespace WebApiHost
{
    public partial class WebApiHostService : ServiceBase
    {
        #region 構造函數
        public WebApiHostService()
        {
            InitializeComponent();
        }
        #endregion

        #region OnStart 啟動服務
        protected override void OnStart(string[] args)
        {
            int port = int.Parse(ConfigurationManager.AppSettings["WebApiServicePort"]);
            StartOptions options = new StartOptions();
            options.Urls.Add("http://127.0.0.1:" + port);
            options.Urls.Add("http://localhost:" + port);
            options.Urls.Add("http://+:" + port);
            WebApp.Start<Startup>(options);
            LogUtil.Log("Web API 服務 啟動成功");
        }
        #endregion

        #region OnStop 停止服務
        protected override void OnStop()
        {
            LogUtil.Log("Web API 服務 停止成功");
            Thread.Sleep(100); //等待一會,待日志寫入檔案
        }
        #endregion

        #region Start 啟動服務
        public void Start()
        {
            OnStart(null);
        }
        #endregion

    }
}      

 配置Web API路由、攔截器以及初始化Swagger線上文檔

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Owin;
using WebApiDemo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;

namespace WebApiHost
{
    class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            HttpConfiguration config = new HttpConfiguration();

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Filters.Add(new MyExceptionFilter());
            config.Filters.Add(new MyActionFilter());

            SwaggerConfig.Register(config);

            appBuilder.UseWebApi(config);
        }
    }
}      

接口實作

1、繼承ApiController

2、RoutePrefix設定路由字首

3、SwaggerResponse用于生成線上文檔描述

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Models;
using Swashbuckle.Swagger.Annotations;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Utils;

namespace WebApiDemo.Controllers
{
    /// <summary>
    /// 測試接口
    /// </summary>
    [RoutePrefix("api/test")]
    public class TestController : ApiController
    {
        #region TestGet 測試GET請求
        /// <summary>
        /// 測試GET請求
        /// </summary>
        /// <param name="val">測試參數</param>
        [HttpGet]
        [Route("TestGet")]
        [SwaggerResponse(HttpStatusCode.OK, "傳回JSON", typeof(JsonListResult<TestGetResult>))]
        public HttpResponseMessage TestGet(string val)
        {
            List<TestGetResult> list = new List<TestGetResult>();

            for (int i = 1; i <= 10; i++)
            {
                TestGetResult item = new TestGetResult();
                item.testValue1 = i.ToString();
                item.testValue2 = i;
                item.testValue3 = "這是傳入參數:" + val;
                list.Add(item);
            }

            var jsonResult = new JsonListResult<TestGetResult>(list, list.Count);

            return ApiHelper.ToJson(jsonResult);
        }
        #endregion

        #region TestPost 測試POST請求
        /// <summary>
        /// 測試POST請求
        /// </summary>
        /// <param name="data">POST資料</param>
        [HttpPost]
        [Route("TestPost")]
        [SwaggerResponse(HttpStatusCode.OK, "傳回JSON", typeof(JsonResult<CommonSubmitResult>))]
        public HttpResponseMessage TestPost([FromBody] TestPostData data)
        {
            JsonResult jsonResult = null;

            if (data == null) return ApiHelper.ToJson(new JsonResult("請檢查參數格式", ResultCode.參數不正确));

            string msg = "操作成功,這是您傳入的參數:" + data.testArg;

            jsonResult = new JsonResult<CommonSubmitResult>(new CommonSubmitResult()
            {
                msg = msg,
                id = "1"
            });

            return ApiHelper.ToJson(jsonResult);
        }
        #endregion

    }

    #region 資料類
    /// <summary>
    /// TestGet接口傳回結果
    /// </summary>
    public class TestGetResult
    {
        /// <summary>
        /// 測試資料1
        /// </summary>
        public string testValue1 { get; set; }

        /// <summary>
        /// 測試資料2
        /// </summary>
        public int testValue2 { get; set; }

        /// <summary>
        /// 測試資料3
        /// </summary>
        public string testValue3 { get; set; }
    }

    /// <summary>
    /// TestPost接口參數
    /// </summary>
    [MyValidate]
    public class TestPostData
    {
        /// <summary>
        /// 測試參數1
        /// </summary>
        [Required]
        public string testArg { get; set; }

        /// <summary>
        /// 測試日期參數
        /// </summary>
        [Required]
        [DateTime(Format = "yyyyMMddHHmmss")]
        public string testTime { get; set; }
    }

    /// <summary>
    /// TestPost接口傳回結果
    /// </summary>
    public class TestPostResult
    {
        /// <summary>
        /// 測試資料1
        /// </summary>
        public string testValue1 { get; set; }
    }
    #endregion

}      

MyValidate屬性表示該資料需要校驗

Required必填校驗

DateTime日期輸入格式校驗

輔助類ApiHelper.cs

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;

namespace Utils
{
    public class ApiHelper
    {
        public static HttpResponseMessage ToJson(object obj)
        {
            string str = JsonConvert.SerializeObject(obj);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str, Encoding.UTF8, "application/json") };
            return result;
        }

    }
}      

 輔助類ServiceHelper.cs

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Utils
{
    /// <summary>
    /// 服務幫助類
    /// </summary>
    public class ServiceHelper
    {
        public static ConcurrentDictionary<Type, object> _dict = new ConcurrentDictionary<Type, object>();

        /// <summary>
        /// 擷取對象
        /// </summary>
        public static T Get<T>() where T : new()
        {
            Type type = typeof(T);
            object obj = _dict.GetOrAdd(type, (key) => new T());

            return (T)obj;
        }

        /// <summary>
        /// 擷取對象
        /// </summary>
        public static T Get<T>(Func<T> func) where T : new()
        {
            Type type = typeof(T);
            object obj = _dict.GetOrAdd(type, (key) => func());

            return (T)obj;
        }

    }
}      

 JsonResult類

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Web;

namespace Models
{
    /// <summary>
    /// Json傳回
    /// </summary>
    public class JsonResult
    {
        /// <summary>
        /// 接口是否成功
        /// </summary>
        [Required]
        public virtual bool success { get; set; }

        /// <summary>
        /// 結果編碼
        /// </summary>
        [Required]
        public virtual ResultCode resultCode { get; set; }

        /// <summary>
        /// 接口錯誤資訊
        /// </summary>
        public virtual string errorMsg { get; set; }

        /// <summary>
        /// 記錄總數(可空類型)
        /// </summary>
        public virtual int? total { get; set; }

        /// <summary>
        /// 預設構造函數
        /// </summary>
        public JsonResult() { }

        /// <summary>
        /// 接口失敗傳回資料
        /// </summary>
        public JsonResult(string errorMsg, ResultCode resultCode)
        {
            this.success = false;
            this.resultCode = resultCode;
            this.errorMsg = errorMsg;
        }

    }

    /// <summary>
    /// Json傳回
    /// </summary>
    public class JsonResult<T> : JsonResult
    {
        /* 子類重寫屬性解決JSON序列化屬性順序問題 */

        /// <summary>
        /// 接口是否成功
        /// </summary>
        [Required]
        public override bool success { get; set; }

        /// <summary>
        /// 結果編碼
        /// </summary>
        [Required]
        public override ResultCode resultCode { get; set; }

        /// <summary>
        /// 接口錯誤資訊
        /// </summary>
        public override string errorMsg { get; set; }

        /// <summary>
        /// 記錄總數(可空類型)
        /// </summary>
        public override int? total { get; set; }

        /// <summary>
        /// 資料
        /// </summary>
        public T info { get; set; }

        /// <summary>
        /// 接口成功傳回資料
        /// </summary>
        public JsonResult(T info)
        {
            this.success = true;
            this.resultCode = ResultCode.OK;
            this.info = info;
            this.total = null;
        }

    }

    /// <summary>
    /// Json傳回
    /// </summary>
    public class JsonListResult<T> : JsonResult
    {
        /* 子類重寫屬性解決JSON序列化屬性順序問題 */

        /// <summary>
        /// 接口是否成功
        /// </summary>
        [Required]
        public override bool success { get; set; }

        /// <summary>
        /// 結果編碼
        /// </summary>
        [Required]
        public override ResultCode resultCode { get; set; }

        /// <summary>
        /// 接口錯誤資訊
        /// </summary>
        public override string errorMsg { get; set; }

        /// <summary>
        /// 記錄總數(可空類型)
        /// </summary>
        public override int? total { get; set; }

        /// <summary>
        /// 資料
        /// </summary>
        public List<T> info { get; set; }

        /// <summary>
        /// 接口成功傳回資料
        /// </summary>
        public JsonListResult(List<T> list, int total)
        {
            this.success = true;
            this.resultCode = ResultCode.OK;
            this.info = list;
            this.total = total;
        }

        /// <summary>
        /// 接口成功傳回資料
        /// </summary>
        public JsonListResult(List<T> list, PagerModel pager)
        {
            this.success = true;
            this.resultCode = ResultCode.OK;
            this.info = list;
            this.total = pager.totalRows;
        }

    }

    /// <summary>
    /// 結果編碼
    /// </summary>
    public enum ResultCode
    {
        OK = 200,

        token不比對或已過期 = 1001,
        請求頭中不存在token = 1002,
        使用者不存在 = 1101,
        密碼不正确 = 1102,
        參數不正确 = 1201,
        操作失敗 = 1301,
        資源不存在 = 1302,
        其他錯誤 = 1401,

        伺服器内部錯誤 = 1501
    }

    /// <summary>
    /// 通用傳回資料
    /// </summary>
    public class CommonSubmitResult
    {
        /// <summary>
        /// 提示資訊
        /// </summary>
        public string msg { get; set; }

        /// <summary>
        /// 記錄ID
        /// </summary>
        public string id { get; set; }
    }

    /// <summary>
    /// 通用傳回資料
    /// </summary>
    public class CommonMsgResult
    {
        /// <summary>
        /// 提示資訊
        /// </summary>
        public string msg { get; set; }
    }
}      

異常攔截器

異常在這裡統一處理,接口方法中不需要再加try catch

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Http.Filters;
using Utils;

namespace WebApiDemo
{
    public class MyExceptionFilter : ExceptionFilterAttribute
    {
        //重寫基類的異常處理方法
        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            var result = new JsonResult("攔截到異常:" + actionExecutedContext.Exception.Message, ResultCode.伺服器内部錯誤);

            LogUtil.Error(actionExecutedContext.Exception);

            actionExecutedContext.Response = ApiHelper.ToJson(result);

            base.OnException(actionExecutedContext);
        }
    }
}      

方法攔截器

1、在攔截器中校驗證token

2、在攔截器中校驗POST和GET參數

3、在攔截器中寫記錄檔

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Microsoft.Owin;
using Models;
using Newtonsoft.Json;
using Swashbuckle.Swagger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Utils;

namespace WebApiDemo
{
    /// <summary>
    /// 攔截器
    /// </summary>
    public class MyActionFilter : ActionFilterAttribute
    {
        #region 變量
        private Dictionary<string, string> _dictActionDesc = ServiceHelper.Get<Dictionary<string, string>>(() => XmlUtil.GetActionDesc());
        #endregion

        #region OnActionExecuting 執行方法前
        /// <summary>
        /// 執行方法前
        /// </summary>
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            base.OnActionExecuting(actionContext);

            //token驗證
            Collection<AllowAnonymousAttribute> attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>();
            if (attributes.Count == 0)
            {
                IEnumerable<string> value;
                if (actionContext.Request.Headers.TryGetValues("token", out value))
                {
                    string token = value.ToArray()[0];

                    if (false) //todo:token驗證
                    {
                        actionContext.Response = ApiHelper.ToJson(new JsonResult("token不比對或已過期", ResultCode.token不比對或已過期));
                        return;
                    }
                }
                else
                {
                    actionContext.Response = ApiHelper.ToJson(new JsonResult("請求頭中不存在token", ResultCode.請求頭中不存在token));
                    return;
                }
            }

            //post參數驗證
            if (actionContext.Request.Method == HttpMethod.Post)
            {
                foreach (string key in actionContext.ActionArguments.Keys)
                {
                    object value = actionContext.ActionArguments[key];
                    if (value != null)
                    {
                        if (value.GetType().GetCustomAttributes(typeof(MyValidateAttribute), false).Length > 0)
                        {
                            string errMsg = null;
                            if (!ValidatePropertyUtil.Validate(value, out errMsg))
                            {
                                JsonResult jsonResult = new JsonResult(errMsg, ResultCode.參數不正确);
                                actionContext.Response = ApiHelper.ToJson(jsonResult);
                                return;
                            }
                        }
                    }
                }
            }

            //get參數驗證
            if (actionContext.Request.Method == HttpMethod.Get)
            {
                foreach (string key in actionContext.ActionArguments.Keys)
                {
                    object value = actionContext.ActionArguments[key];
                    if (value != null)
                    {
                        if (key == "page")
                        {
                            if ((int)value <= 0)
                            {
                                JsonResult jsonResult = new JsonResult("page必須大于0", ResultCode.參數不正确);
                                actionContext.Response = ApiHelper.ToJson(jsonResult);
                                return;
                            }
                        }

                        if (key == "pageSize")
                        {
                            if ((int)value > 10000)
                            {
                                JsonResult jsonResult = new JsonResult("pageSize大于10000,請分頁查詢", ResultCode.參數不正确);
                                actionContext.Response = ApiHelper.ToJson(jsonResult);
                                return;
                            }
                        }
                    }
                }
            }
        }
        #endregion

        #region OnActionExecutedAsync 執行方法後
        /// <summary>
        /// 執行方法後
        /// </summary>
        public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
        {
            return Task.Factory.StartNew(async () =>
            {
                try
                {
                    Type controllerType = actionExecutedContext.ActionContext.ControllerContext.Controller.GetType();
                    MethodInfo methodInfo = controllerType.GetMethod(actionExecutedContext.ActionContext.ActionDescriptor.ActionName);

                    string action = controllerType.FullName + "." + methodInfo.Name;

                    if (_dictActionDesc.ContainsKey(action))
                    {
                        string jsonResult = null;
                        List<string> paramList = new List<string>();
                        string param = string.Empty;
                        if (actionExecutedContext.Request.Method == HttpMethod.Post)
                        {
                            foreach (string key in actionExecutedContext.ActionContext.ActionArguments.Keys)
                            {
                                object value = actionExecutedContext.ActionContext.ActionArguments[key];
                                if (value != null && value as HttpRequestMessage == null)
                                {
                                    paramList.Add(JsonConvert.SerializeObject(value));
                                }
                            }
                            param = string.Join(",", paramList);

                            if (actionExecutedContext.Exception == null)
                            {
                                byte[] bArr = await actionExecutedContext.ActionContext.Response.Content.ReadAsByteArrayAsync();
                                jsonResult = Encoding.UTF8.GetString(bArr);
                            }
                            else
                            {
                                JsonResult jr = new JsonResult(actionExecutedContext.Exception.Message + "\r\n" + actionExecutedContext.Exception.StackTrace, ResultCode.伺服器内部錯誤);
                                jsonResult = JsonConvert.SerializeObject(jr);
                            }
                        }
                        else
                        {
                            foreach (string key in actionExecutedContext.ActionContext.ActionArguments.Keys)
                            {
                                object value = actionExecutedContext.ActionContext.ActionArguments[key];
                                if (value != null)
                                {
                                    paramList.Add(key + "=" + value.ToString());
                                }
                                else
                                {
                                    paramList.Add(key + "=");
                                }
                            }
                            param = string.Join("&", paramList);

                            if (actionExecutedContext.Exception == null)
                            {
                                if (actionExecutedContext.ActionContext.Response.Content is StringContent)
                                {
                                    byte[] bArr = await actionExecutedContext.ActionContext.Response.Content.ReadAsByteArrayAsync();
                                    jsonResult = Encoding.UTF8.GetString(bArr);
                                }
                                else
                                {
                                    jsonResult = JsonConvert.SerializeObject(new JsonResult<object>(null));
                                }
                            }
                            else
                            {
                                JsonResult jr = new JsonResult(actionExecutedContext.Exception.Message + "\r\n" + actionExecutedContext.Exception.StackTrace, ResultCode.伺服器内部錯誤);
                                jsonResult = JsonConvert.SerializeObject(jr);
                            }
                        }

                        string ip = null;
                        if (actionExecutedContext.Request.Properties.ContainsKey("MS_OwinContext"))
                        {
                            OwinContext owinContext = actionExecutedContext.Request.Properties["MS_OwinContext"] as OwinContext;
                            if (owinContext != null)
                            {
                                try
                                {
                                    ip = owinContext.Request.RemoteIpAddress;
                                }
                                catch { }
                            }
                        }

                        //todo:寫記錄檔
                        /*
                        ServiceHelper.Get<SysOperLogDal>().Log(action, //方法名
                            actionExecutedContext.Request.Method, //請求類型
                            _dictActionDesc[action], //方法注釋
                            ip, //IP
                            actionExecutedContext.Request.RequestUri.LocalPath, //URL
                            param, //請求參數
                            jsonResult); //操作結果
                        */
                    }
                }
                catch (Exception ex)
                {
                    LogUtil.Error(ex, "MyActionFilter OnActionExecutedAsync 寫記錄檔出錯");
                }
            });
        }
        #endregion

    }
}      

參數校驗工具類

這裡隻做了必填和日期校驗,且字段類型隻是基礎類型,有待完善

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web;

namespace Utils
{
    /// <summary>
    /// 字段屬性驗證工具類
    /// </summary>
    public class ValidatePropertyUtil
    {
        /// <summary>
        /// 驗證資料 
        /// true:驗證通過 false 驗證不通過
        /// </summary>
        /// <param name="data">資料</param>
        /// <param name="errMsg">錯誤資訊</param>
        public static bool Validate(object data, out string errMsg)
        {
            PropertyInfo[] propertyInfoList = data.GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in propertyInfoList)
            {
                if (propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), false).Length > 0)
                {
                    object value = propertyInfo.GetValue(data);
                    if (value == null)
                    {
                        errMsg = "屬性 " + propertyInfo.Name + " 必填";
                        return false;
                    }
                }

                object[] attrArr = propertyInfo.GetCustomAttributes(typeof(DateTimeAttribute), false);
                if (attrArr.Length > 0)
                {
                    DateTimeAttribute attr = attrArr[0] as DateTimeAttribute;
                    object value = propertyInfo.GetValue(data);
                    if (value == null)
                    {
                        errMsg = "屬性 " + propertyInfo.Name + " 是日期時間格式,格式:" + attr.Format;
                        return false;
                    }
                    else
                    {
                        DateTime dt;
                        if (!DateTime.TryParseExact(value.ToString(), attr.Format, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
                        {
                            errMsg = "屬性 " + propertyInfo.Name + " 是日期時間格式,格式:" + attr.Format;
                            return false;
                        }
                    }
                }
            }

            errMsg = null;
            return true;
        }
    }
}      

swagger.js

複制到輸出目錄:不複制

生成操作:嵌入的資源

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
var SwaggerTranslator = (function () {
    //定時執行檢測是否轉換成中文,最多執行500次  即500*50/1000=25s
    var _iexcute = 0;

    var _lock = false;

    //中文語言包
    var _words = {
        "Warning: Deprecated": "警告:已過時",
        "Implementation Notes": "實作備注",
        "Response Class": "響應類",
        "Status": "狀态",
        "Parameters": "參數",
        "Parameter": "參數",
        "Value": "值",
        "Description": "描述",
        "Parameter Type": "參數類型",
        "Data Type": "資料類型",
        "Response Messages": "響應消息",
        "HTTP Status Code": "HTTP狀态碼",
        "Reason": "原因",
        "Response Model": "響應模型",
        "Request URL": "請求URL",
        "Response Body": "響應體",
        "Response Code": "響應碼",
        "Response Headers": "響應頭",
        "Hide Response": "隐藏響應",
        "Headers": "頭",
        "Try it out!": "試一下!",
        "Example Value": "示例值",
        "Show/Hide": "顯示/隐藏",
        "List Operations": "顯示操作",
        "Expand Operations": "展開操作",
        "Raw": "原始",
        "can't parse JSON.  Raw result": "無法解析JSON. 原始結果",
        "Model Schema": "模型架構",
        "Model": "模型",
        "apply": "應用",
        "Username": "使用者名",
        "Password": "密碼",
        "Terms of service": "服務條款",
        "Created by": "建立者",
        "See more at": "檢視更多:",
        "Contact the developer": "聯系開發者",
        "api version": "api版本",
        "Response Content Type": "響應Content Type",
        "fetching resource": "正在擷取資源",
        "fetching resource list": "正在擷取資源清單",
        "Explore": "浏覽",
        "Show Swagger Petstore Example Apis": "顯示 Swagger Petstore 示例 Apis",
        "Can't read from server.  It may not have the appropriate access-control-origin settings.": "無法從伺服器讀取。可能沒有正确設定access-control-origin。",
        "Please specify the protocol for": "請指定協定:",
        "Can't read swagger JSON from": "無法讀取swagger JSON于",
        "Finished Loading Resource Information. Rendering Swagger UI": "已加載資源資訊。正在渲染Swagger UI",
        "Unable to read api": "無法讀取api",
        "from path": "從路徑",
        "Click to set as parameter value": "點選設定參數",
        "server returned": "伺服器傳回"
    };

    //定時執行轉換
    var _translator2Cn = function () {
        if ($("#resources_container .resource").length > 0) {
            _tryTranslate();
        }

        if ($("#explore").text() == "Explore" && _iexcute < 500) {
            _iexcute++;
            setTimeout(_translator2Cn, 50);
        }
    };

    //設定控制器注釋
    var _setControllerSummary = function () {
        if (!_lock) {
            _lock = true;
            $.ajax({
                type: "get",
                async: true,
                url: $("#input_baseUrl").val(),
                dataType: "json",
                success: function (data) {
                    var summaryDict = data.ControllerDesc;
                    var id, controllerName, strSummary;
                    $("#resources_container .resource").each(function (i, item) {
                        id = $(item).attr("id");
                        if (id) {
                            controllerName = id.substring(9);
                            strSummary = summaryDict[controllerName];
                            if (strSummary) {
                                $(item).children(".heading").children(".options").prepend('<li class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
                            }
                        }
                    });

                    setTimeout(function () {
                        _lock = false;
                    }, 100);
                }
            });
        }
    };

    //嘗試将英文轉換成中文
    var _tryTranslate = function () {
        $('[data-sw-translate]').each(function () {
            $(this).html(_getLangDesc($(this).html()));
            $(this).val(_getLangDesc($(this).val()));
            $(this).attr('title', _getLangDesc($(this).attr('title')));
        });
    };

    var _getLangDesc = function (word) {
        return _words[$.trim(word)] !== undefined ? _words[$.trim(word)] : word;
    };

    return {
        translate: function () {
            document.title = "API描述文檔";
            $('body').append('<style type="text/css">.controller-summary{color:#10a54a !important;word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:250px;text-align:right;cursor:default;} </style>');
            $("#logo").html("接口描述").attr("href", "/swagger/ui/index");
            //設定控制器描述
            _setControllerSummary();
            _translator2Cn();
        }
    };
})();

//執行轉換
SwaggerTranslator.translate();      

CachingSwaggerProvider.cs

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Swashbuckle.Swagger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml;

namespace WebApiDemo
{
    /// <summary>
    /// 用于漢化Swagger
    /// </summary>
    public class CachingSwaggerProvider : ISwaggerProvider
    {
        private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();

        private readonly ISwaggerProvider _swaggerProvider;

        /// <summary>
        /// 構造函數
        /// </summary>
        public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
            _swaggerProvider = swaggerProvider;
        }

        /// <summary>
        /// GetSwagger
        /// </summary>
        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
            try
            {
                var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
                SwaggerDocument srcDoc = null;
                //隻讀取一次
                if (!_cache.TryGetValue(cacheKey, out srcDoc))
                {
                    srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);

                    srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
                    _cache.TryAdd(cacheKey, srcDoc);
                }
                return srcDoc;
            }
            catch
            {
                SwaggerDocument doc = new SwaggerDocument();
                doc.info = new Info();
                doc.info.title = "接口不存在";
                return doc;
            }
        }

        /// <summary>
        /// 從API文檔中讀取控制器描述
        /// </summary>
        /// <returns>所有控制器描述</returns>
        public static ConcurrentDictionary<string, string> GetControllerDesc()
        {
            string xmlpath = string.Format("{0}/{1}.XML", System.AppDomain.CurrentDomain.BaseDirectory, typeof(SwaggerConfig).Assembly.GetName().Name);
            ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
            if (File.Exists(xmlpath))
            {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(xmlpath);
                string type = string.Empty, path = string.Empty, controllerName = string.Empty;

                string[] arrPath;
                int length = -1, cCount = "Controller".Length;
                XmlNode summaryNode = null;
                foreach (XmlNode node in xmldoc.SelectNodes("//member"))
                {
                    type = node.Attributes["name"].Value;
                    if (type.StartsWith("T:"))
                    {
                        //控制器
                        arrPath = type.Split('.');
                        length = arrPath.Length;
                        controllerName = arrPath[length - 1];
                        if (controllerName.EndsWith("Controller"))
                        {
                            //擷取控制器注釋
                            summaryNode = node.SelectSingleNode("summary");
                            string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                            if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                            {
                                controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                            }
                        }
                    }
                }
            }
            return controllerDescDict;
        }

    }
}      

SwaggerOperationFilter.cs

檔案上傳與token參數

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using Swashbuckle.Swagger;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Http.Description;

namespace WebApiDemo
{
    public class SwaggerOperationFilter : IOperationFilter
    {
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
        {
            if (operation.parameters == null) operation.parameters = new List<Parameter>();

            if (apiDescription.RelativePath.Contains("/UploadFile"))
            {
                operation.parameters.RemoveAt(0);

                operation.parameters.Add(new Parameter
                {
                    name = "folder",
                    @in = "formData",
                    description = "檔案夾",
                    required = false,
                    type = "string"
                });

                operation.parameters.Add(new Parameter
                {
                    name = "file",
                    @in = "formData",
                    description = "檔案",
                    required = true,
                    type = "file"
                });

                operation.consumes.Add("multipart/form-data");
            }

            Collection<AllowAnonymousAttribute> attributes = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>();
            if (attributes.Count == 0)
            {
                operation.parameters.Insert(0, new Parameter { name = "token", @in = "header", description = "Token", required = true, type = "string" });
            }
        }
    }
}      

SwaggerConfig.cs

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using System.Web.Http;
using Swashbuckle.Application;
using System.IO;
using WebApiDemo;
using System.Web;

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]

namespace WebApiDemo
{
    public class SwaggerConfig
    {
        public static void Register(HttpConfiguration config)
        {
            var thisAssembly = typeof(SwaggerConfig).Assembly;

            config
                .EnableSwagger(c =>
                    {
                        // By default, the service root url is inferred from the request used to access the docs.
                        // However, there may be situations (e.g. proxy and load-balanced environments) where this does not
                        // resolve correctly. You can workaround this by providing your own code to determine the root URL.
                        //
                        //c.RootUrl(req => GetRootUrlFromAppConfig());

                        // If schemes are not explicitly provided in a Swagger 2.0 document, then the scheme used to access
                        // the docs is taken as the default. If your API supports multiple schemes and you want to be explicit
                        // about them, you can use the "Schemes" option as shown below.
                        //
                        //c.Schemes(new[] { "http", "https" });

                        // Use "SingleApiVersion" to describe a single version API. Swagger 2.0 includes an "Info" object to
                        // hold additional metadata for an API. Version and title are required but you can also provide
                        // additional fields by chaining methods off SingleApiVersion.
                        //
                        c.SingleApiVersion("v1", "WebApiDemo 測試接口文檔");

                        c.OperationFilter<SwaggerOperationFilter>(); //添加過濾器,增加Token令牌驗證

                        c.IncludeXmlComments(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"WebApiDemo.XML"));

                        c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider)); //漢化Swagger兩步:第一步

                        // If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option.
                        //
                        //c.PrettyPrint();

                        // If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion".
                        // In this case, you must provide a lambda that tells Swashbuckle which actions should be
                        // included in the docs for a given API version. Like "SingleApiVersion", each call to "Version"
                        // returns an "Info" builder so you can provide additional metadata per API version.
                        //
                        //c.MultipleApiVersions(
                        //    (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
                        //    (vc) =>
                        //    {
                        //        vc.Version("v2", "Swashbuckle Dummy API V2");
                        //        vc.Version("v1", "Swashbuckle Dummy API V1");
                        //    });

                        // You can use "BasicAuth", "ApiKey" or "OAuth2" options to describe security schemes for the API.
                        // See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details.
                        // NOTE: These only define the schemes and need to be coupled with a corresponding "security" property
                        // at the document or operation level to indicate which schemes are required for an operation. To do this,
                        // you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties
                        // according to your specific authorization implementation
                        //
                        //c.BasicAuth("basic")
                        //    .Description("Basic HTTP Authentication");
                        //
                        // NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section
                        //c.ApiKey("apiKey")
                        //    .Description("API Key Authentication")
                        //    .Name("apiKey")
                        //    .In("header");
                        //
                        //c.OAuth2("oauth2")
                        //    .Description("OAuth2 Implicit Grant")
                        //    .Flow("implicit")
                        //    .AuthorizationUrl("http://petstore.swagger.wordnik.com/api/oauth/dialog")
                        //    //.TokenUrl("https://tempuri.org/token")
                        //    .Scopes(scopes =>
                        //    {
                        //        scopes.Add("read", "Read access to protected resources");
                        //        scopes.Add("write", "Write access to protected resources");
                        //    });

                        // Set this flag to omit descriptions for any actions decorated with the Obsolete attribute
                        //c.IgnoreObsoleteActions();

                        // Each operation be assigned one or more tags which are then used by consumers for various reasons.
                        // For example, the swagger-ui groups operations according to the first tag of each operation.
                        // By default, this will be controller name but you can use the "GroupActionsBy" option to
                        // override with any value.
                        //
                        //c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());

                        // You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate
                        // the order in which operations are listed. For example, if the default grouping is in place
                        // (controller name) and you specify a descending alphabetic sort order, then actions from a
                        // ProductsController will be listed before those from a CustomersController. This is typically
                        // used to customize the order of groupings in the swagger-ui.
                        //
                        //c.OrderActionGroupsBy(new DescendingAlphabeticComparer());

                        // If you annotate Controllers and API Types with
                        // Xml comments (http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx), you can incorporate
                        // those comments into the generated docs and UI. You can enable this by providing the path to one or
                        // more Xml comment files.
                        //
                        //c.IncludeXmlComments(GetXmlCommentsPath());

                        // Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types
                        // exposed in your API. However, there may be occasions when more control of the output is needed.
                        // This is supported through the "MapType" and "SchemaFilter" options:
                        //
                        // Use the "MapType" option to override the Schema generation for a specific type.
                        // It should be noted that the resulting Schema will be placed "inline" for any applicable Operations.
                        // While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not.
                        // It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only
                        // use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a
                        // complex Schema, use a Schema filter.
                        //
                        //c.MapType<ProductType>(() => new Schema { type = "integer", format = "int32" });

                        // If you want to post-modify "complex" Schemas once they've been generated, across the board or for a
                        // specific type, you can wire up one or more Schema filters.
                        //
                        //c.SchemaFilter<ApplySchemaVendorExtensions>();

                        // In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique
                        // Schema Id. By default, Swashbuckle does NOT use the full type name in Schema Ids. In most cases, this
                        // works well because it prevents the "implementation detail" of type namespaces from leaking into your
                        // Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll
                        // need to opt out of this behavior to avoid Schema Id conflicts.
                        //
                        //c.UseFullTypeNameInSchemaIds();

                        // Alternatively, you can provide your own custom strategy for inferring SchemaId's for
                        // describing "complex" types in your API.
                        //
                        //c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName);

                        // Set this flag to omit schema property descriptions for any type properties decorated with the
                        // Obsolete attribute
                        //c.IgnoreObsoleteProperties();

                        // In accordance with the built in JsonSerializer, Swashbuckle will, by default, describe enums as integers.
                        // You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given
                        // enum type. Swashbuckle will honor this change out-of-the-box. However, if you use a different
                        // approach to serialize enums as strings, you can also force Swashbuckle to describe them as strings.
                        //
                        //c.DescribeAllEnumsAsStrings();

                        // Similar to Schema filters, Swashbuckle also supports Operation and Document filters:
                        //
                        // Post-modify Operation descriptions once they've been generated by wiring up one or more
                        // Operation filters.
                        //
                        //c.OperationFilter<AddDefaultResponse>();
                        //
                        // If you've defined an OAuth2 flow as described above, you could use a custom filter
                        // to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required
                        // to execute the operation
                        //
                        //c.OperationFilter<AssignOAuth2SecurityRequirements>();

                        // Post-modify the entire Swagger document by wiring up one or more Document filters.
                        // This gives full control to modify the final SwaggerDocument. You should have a good understanding of
                        // the Swagger 2.0 spec. - https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md
                        // before using this option.
                        //
                        //c.DocumentFilter<ApplyDocumentVendorExtensions>();

                        // In contrast to WebApi, Swagger 2.0 does not include the query string component when mapping a URL
                        // to an action. As a result, Swashbuckle will raise an exception if it encounters multiple actions
                        // with the same path (sans query string) and HTTP method. You can workaround this by providing a
                        // custom strategy to pick a winner or merge the descriptions for the purposes of the Swagger docs
                        //
                        //c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());

                        // Wrap the default SwaggerGenerator with additional behavior (e.g. caching) or provide an
                        // alternative implementation for ISwaggerProvider with the CustomProvider option.
                        //
                        //c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
                    })
                .EnableSwaggerUi(c =>
                    {
                        // Use the "DocumentTitle" option to change the Document title.
                        // Very helpful when you have multiple Swagger pages open, to tell them apart.
                        //
                        //c.DocumentTitle("My Swagger UI");

                        // Use the "InjectStylesheet" option to enrich the UI with one or more additional CSS stylesheets.
                        // The file must be included in your project as an "Embedded Resource", and then the resource's
                        // "Logical Name" is passed to the method as shown below.
                        //
                        //c.InjectStylesheet(containingAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testStyles1.css");

                        // Use the "InjectJavaScript" option to invoke one or more custom JavaScripts after the swagger-ui
                        // has loaded. The file must be included in your project as an "Embedded Resource", and then the resource's
                        // "Logical Name" is passed to the method as shown above.
                        //
                        //c.InjectJavaScript(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testScript1.js");

                        // The swagger-ui renders boolean data types as a dropdown. By default, it provides "true" and "false"
                        // strings as the possible choices. You can use this option to change these to something else,
                        // for example 0 and 1.
                        //
                        //c.BooleanValues(new[] { "0", "1" });

                        // By default, swagger-ui will validate specs against swagger.io's online validator and display the result
                        // in a badge at the bottom of the page. Use these options to set a different validator URL or to disable the
                        // feature entirely.
                        //c.SetValidatorUrl("http://localhost/validator");
                        //c.DisableValidator();

                        // Use this option to control how the Operation listing is displayed.
                        // It can be set to "None" (default), "List" (shows operations for each resource),
                        // or "Full" (fully expanded: shows operations and their details).
                        //
                        //c.DocExpansion(DocExpansion.List);

                        // Specify which HTTP operations will have the 'Try it out!' option. An empty paramter list disables
                        // it for all operations.
                        //
                        //c.SupportedSubmitMethods("GET", "HEAD");

                        // Use the CustomAsset option to provide your own version of assets used in the swagger-ui.
                        // It's typically used to instruct Swashbuckle to return your version instead of the default
                        // when a request is made for "index.html". As with all custom content, the file must be included
                        // in your project as an "Embedded Resource", and then the resource's "Logical Name" is passed to
                        // the method as shown below.
                        //
                        //c.CustomAsset("index", containingAssembly, "YourWebApiProject.SwaggerExtensions.index.html");

                        // If your API has multiple versions and you've applied the MultipleApiVersions setting
                        // as described above, you can also enable a select box in the swagger-ui, that displays
                        // a discovery URL for each version. This provides a convenient way for users to browse documentation
                        // for different API versions.
                        //
                        //c.EnableDiscoveryUrlSelector();

                        // If your API supports the OAuth2 Implicit flow, and you've described it correctly, according to
                        // the Swagger 2.0 specification, you can enable UI support as shown below.
                        //
                        //c.EnableOAuth2Support(
                        //    clientId: "test-client-id",
                        //    clientSecret: null,
                        //    realm: "test-realm",
                        //    appName: "Swagger UI"
                        //    //additionalQueryStringParams: new Dictionary<string, string>() { { "foo", "bar" } }
                        //);

                        // If your API supports ApiKey, you can override the default values.
                        // "apiKeyIn" can either be "query" or "header"
                        //
                        //c.EnableApiKeySupport("apiKey", "header");

                        c.InjectJavaScript(thisAssembly, "WebApiDemo.Swagger.swagger.js"); //漢化Swagger兩步:第二步
                    });
        }
    }
}      

輔助類XmlUtil.cs

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml;

namespace Utils
{
    /// <summary>
    /// XML工具類
    /// </summary>
    public class XmlUtil
    {
        /// <summary>
        /// 從XML讀取注釋
        /// </summary>
        /// <returns></returns>
        public static Dictionary<string, string> GetActionDesc()
        {
            Dictionary<string, string> result = new Dictionary<string, string>();

            string xmlPath = string.Format("{0}/{1}.XML", System.AppDomain.CurrentDomain.BaseDirectory, typeof(XmlUtil).Assembly.GetName().Name);
            if (File.Exists(xmlPath))
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(xmlPath);

                XmlNode summaryNode; string type; string desc; int pos; string key;
                foreach (XmlNode node in xmlDoc.SelectNodes("//member"))
                {
                    type = type = node.Attributes["name"].Value;
                    if (type.StartsWith("M:PrisonWebApi.Controllers"))
                    {
                        pos = type.IndexOf("(");
                        if (pos == -1) pos = type.Length;
                        key = type.Substring(2, pos - 2);
                        summaryNode = node.SelectSingleNode("summary");
                        desc = summaryNode.InnerText.Trim();
                        result.Add(key, desc);
                    }
                }
            }

            return result;
        }
    }
}      

WebApiHost工程的App.config

WebApiDemo工程的Global.asax.cs和Web.config檔案沒有用了 

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <appSettings>
    <!--Web API 服務端口号-->
    <add key="WebApiServicePort" value="8500" />
  </appSettings>
</configuration>      

 線上文檔截圖

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔
ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 線上文檔