• 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
        {
            /// <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服务

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

     JsonResult类

    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; }
        }
    }
    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
    {
        /// <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 + "
    " + 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 + "
    " + 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
    
        }
    }
    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
    {
        /// <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;
            }
        }
    }
    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('<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-250px;text-align:right;cursor:default;} </style>');
                $("#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
    {
        /// <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;
            }
    
        }
    }
    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<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" });
                }
            }
        }
    }
    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<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两步:第二步
                        });
            }
        }
    }
    View Code

    辅助类XmlUtil.cs

    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;
            }
        }
    }
    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>
        <!--Web API 服务端口号-->
        <add key="WebApiServicePort" value="8500" />
      </appSettings>
    </configuration>
    View Code

     在线文档截图

  • 相关阅读:
    2016 年青岛网络赛---Sort(k叉哈夫曼)
    Gym 100703G---Game of numbers(DP)
    棋盘覆盖(分治法)
    大整数乘法(分治法)
    博客编辑---数学公式
    《程序员代码面试指南》第八章 数组和矩阵问题 子数组的最大累加和问题
    《程序员代码面试指南》第八章 数组和矩阵问题 奇数下标都是奇数或者偶数下标都是偶数
    《程序员代码面试指南》第八章 数组和矩阵问题 自然数数组的排序
    《程序员代码面试指南》第八章 数组和矩阵问题 计算数组的小和
    《程序员代码面试指南》第八章 数组和矩阵问题 未排序数组中累加和小于或等于给定值的最长子数组长度
  • 原文地址:https://www.cnblogs.com/s0611163/p/13175721.html
Copyright © 2020-2023  润新知