ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档

新建Web API工程

 

选Empty,勾选Web API,不要选择Web API,那样会把MVC勾上,这里不需要MVC

Web API工程属性

 XML文件用于生成在线文档

  新建Windows服务作为Web API的宿主

 

WebApiHost工程属性

 控制台应用程序方便调试

 Windows服务安装Microsoft.AspNet.WebApi.OwinSelfHost

 

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

 WebApiDemo安装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
    {
        /// 
        /// 应用程序的主入口点。
        /// 
        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服务

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(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

    }
}
View Code

 配置Web API路由、拦截器以及初始化Swagger在线文档

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);
        }
    }
}
View Code

接口实现

1、继承ApiController

2、RoutePrefix设置路由前缀

3、SwaggerResponse用于生成在线文档描述

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
{
    /// 
    /// 测试接口
    /// 
    [RoutePrefix("api/test")]
    public class TestController : ApiController
    {
        #region TestGet 测试GET请求
        /// 
        /// 测试GET请求
        /// 
        /// 测试参数
        [HttpGet]
        [Route("TestGet")]
        [SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonListResult))]
        public HttpResponseMessage TestGet(string val)
        {
            List list = new List();

            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(list, list.Count);

            return ApiHelper.ToJson(jsonResult);
        }
        #endregion

        #region TestPost 测试POST请求
        /// 
        /// 测试POST请求
        /// 
        /// POST数据
        [HttpPost]
        [Route("TestPost")]
        [SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonResult))]
        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(new CommonSubmitResult()
            {
                msg = msg,
                id = "1"
            });

            return ApiHelper.ToJson(jsonResult);
        }
        #endregion

    }

    #region 数据类
    /// 
    /// TestGet接口返回结果
    /// 
    public class TestGetResult
    {
        /// 
        /// 测试数据1
        /// 
        public string testValue1 { get; set; }

        /// 
        /// 测试数据2
        /// 
        public int testValue2 { get; set; }

        /// 
        /// 测试数据3
        /// 
        public string testValue3 { get; set; }
    }

    /// 
    /// TestPost接口参数
    /// 
    [MyValidate]
    public class TestPostData
    {
        /// 
        /// 测试参数1
        /// 
        [Required]
        public string testArg { get; set; }

        /// 
        /// 测试日期参数
        /// 
        [Required]
        [DateTime(Format = "yyyyMMddHHmmss")]
        public string testTime { get; set; }
    }

    /// 
    /// TestPost接口返回结果
    /// 
    public class TestPostResult
    {
        /// 
        /// 测试数据1
        /// 
        public string testValue1 { get; set; }
    }
    #endregion

}
View Code

MyValidate属性表示该数据需要校验

Required必填校验
DateTime日期输入格式校验

辅助类ApiHelper.cs

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;
        }

    }
}
View Code

 辅助类ServiceHelper.cs

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Utils
{
    /// 
    /// 服务帮助类
    /// 
    public class ServiceHelper
    {
        public static ConcurrentDictionaryobject> _dict = new ConcurrentDictionaryobject>();

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

            return (T)obj;
        }

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

            return (T)obj;
        }

    }
}
View Code

 JsonResult类

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Web;

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

        /// 
        /// 结果编码
        /// 
        [Required]
        public virtual ResultCode resultCode { get; set; }

        /// 
        /// 接口错误信息
        /// 
        public virtual string errorMsg { get; set; }

        /// 
        /// 记录总数(可空类型)
        /// 
        public virtual int? total { get; set; }

        /// 
        /// 默认构造函数
        /// 
        public JsonResult() { }

        /// 
        /// 接口失败返回数据
        /// 
        public JsonResult(string errorMsg, ResultCode resultCode)
        {
            this.success = false;
            this.resultCode = resultCode;
            this.errorMsg = errorMsg;
        }

    }

    /// 
    /// Json返回
    /// 
    public class JsonResult : JsonResult
    {
        /* 子类重写属性解决JSON序列化属性顺序问题 */

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

        /// 
        /// 结果编码
        /// 
        [Required]
        public override ResultCode resultCode { get; set; }

        /// 
        /// 接口错误信息
        /// 
        public override string errorMsg { get; set; }

        /// 
        /// 记录总数(可空类型)
        /// 
        public override int? total { get; set; }

        /// 
        /// 数据
        /// 
        public T info { get; set; }

        /// 
        /// 接口成功返回数据
        /// 
        public JsonResult(T info)
        {
            this.success = true;
            this.resultCode = ResultCode.OK;
            this.info = info;
            this.total = null;
        }

    }

    /// 
    /// Json返回
    /// 
    public class JsonListResult : JsonResult
    {
        /* 子类重写属性解决JSON序列化属性顺序问题 */

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

        /// 
        /// 结果编码
        /// 
        [Required]
        public override ResultCode resultCode { get; set; }

        /// 
        /// 接口错误信息
        /// 
        public override string errorMsg { get; set; }

        /// 
        /// 记录总数(可空类型)
        /// 
        public override int? total { get; set; }

        /// 
        /// 数据
        /// 
        public List info { get; set; }

        /// 
        /// 接口成功返回数据
        /// 
        public JsonListResult(List list, int total)
        {
            this.success = true;
            this.resultCode = ResultCode.OK;
            this.info = list;
            this.total = total;
        }

        /// 
        /// 接口成功返回数据
        /// 
        public JsonListResult(List list, PagerModel pager)
        {
            this.success = true;
            this.resultCode = ResultCode.OK;
            this.info = list;
            this.total = pager.totalRows;
        }

    }

    /// 
    /// 结果编码
    /// 
    public enum ResultCode
    {
        OK = 200,

        token不匹配或已过期 = 1001,
        请求头中不存在token = 1002,
        用户不存在 = 1101,
        密码不正确 = 1102,
        参数不正确 = 1201,
        操作失败 = 1301,
        资源不存在 = 1302,
        其他错误 = 1401,

        服务器内部错误 = 1501
    }

    /// 
    /// 通用返回数据
    /// 
    public class CommonSubmitResult
    {
        /// 
        /// 提示信息
        /// 
        public string msg { get; set; }

        /// 
        /// 记录ID
        /// 
        public string id { get; set; }
    }

    /// 
    /// 通用返回数据
    /// 
    public class CommonMsgResult
    {
        /// 
        /// 提示信息
        /// 
        public string msg { get; set; }
    }
}
View Code

异常拦截器

异常在这里统一处理,接口方法中不需要再加try catch

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);
        }
    }
}
View Code

方法拦截器

1、在拦截器中校验证token
2、在拦截器中校验POST和GET参数
3、在拦截器中写操作日志

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
{
    /// 
    /// 拦截器
    /// 
    public class MyActionFilter : ActionFilterAttribute
    {
        #region 变量
        private Dictionary<string, string> _dictActionDesc = ServiceHelper.Getstring, string>>(() => XmlUtil.GetActionDesc());
        #endregion

        #region OnActionExecuting 执行方法前
        /// 
        /// 执行方法前
        /// 
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            base.OnActionExecuting(actionContext);

            //token验证
            Collection attributes = actionContext.ActionDescriptor.GetCustomAttributes();
            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 执行方法后
        /// 
        /// 执行方法后
        /// 
        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().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

    }
}
View Code

参数校验工具类

这里只做了必填和日期校验,且字段类型只是基础类型,有待完善

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
{
    /// 
    /// 字段属性验证工具类
    /// 
    public class ValidatePropertyUtil
    {
        /// 
        /// 验证数据 
        /// true:验证通过 false 验证不通过
        /// 
        /// 数据
        /// 错误信息
        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;
        }
    }
}
View Code

swagger.js

复制到输出目录:不复制

生成操作:嵌入的资源

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('
  • ' + strSummary + '
  • '); } } }); 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(''); $("#logo").html("接口描述").attr("href", "/swagger/ui/index"); //设置控制器描述 _setControllerSummary(); _translator2Cn(); } }; })(); //执行转换 SwaggerTranslator.translate();
    View Code

    CachingSwaggerProvider.cs

    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
    {
        /// 
        /// 用于汉化Swagger
        /// 
        public class CachingSwaggerProvider : ISwaggerProvider
        {
            private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();
    
            private readonly ISwaggerProvider _swaggerProvider;
    
            /// 
            /// 构造函数
            /// 
            public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
            {
                _swaggerProvider = swaggerProvider;
            }
    
            /// 
            /// GetSwagger
            /// 
            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;
                }
            }
    
            /// 
            /// 从API文档中读取控制器描述
            /// 
            /// 所有控制器描述
            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;
            }
    
        }
    }
    View Code

    SwaggerOperationFilter.cs

    文件上传与token参数

    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();
    
                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 attributes = apiDescription.ActionDescriptor.GetCustomAttributes();
                if (attributes.Count == 0)
                {
                    operation.parameters.Insert(0, new Parameter { name = "token", @in = "header", description = "Token", required = true, type = "string" });
                }
            }
        }
    }
    View Code

    SwaggerConfig.cs

    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(); //添加过滤器,增加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(() => 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();
    
                            // 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();
                            //
                            // 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();
    
                            // 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();
    
                            // 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() { { "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两步:第二步
                        });
            }
        }
    }
    View Code

    辅助类XmlUtil.cs

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Web;
    using System.Xml;
    
    namespace Utils
    {
        /// 
        /// XML工具类
        /// 
        public class XmlUtil
        {
            /// 
            /// 从XML读取注释
            /// 
            /// 
            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;
            }
        }
    }
    View Code

    WebApiHost工程的App.config

    WebApiDemo工程的Global.asax.cs和Web.config文件没有用了 

    xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
      startup>
      <appSettings>
        
        <add key="WebApiServicePort" value="8500" />
      appSettings>
    configuration>
    View Code

     在线文档截图

     

    你可能感兴趣的:(ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档)