使用WPF、OwinSelfHost和Swagger创建自托管的Web API
在本篇博客中,我将介绍如何在WPF应用程序中使用OwinSelfHost和Swagger来创建自托管的Web API。我们将使用WPF作为我们的应用程序界面,OwinSelfHost来自托管我们的Web API,并使用Swagger来为我们的API生成文档。
首先,确保你的计算机上已安装了以下组件:
- Visual Studio2017
- .NET Framework(至少需要4.5版本)
接下来,按照以下步骤进行操作:
步骤1:创建新的WPF项目 在Visual Studio中创建一个新的WPF项目。命名它为"SwaggerBlog"。
步骤2:安装必要的NuGet包 在解决方案资源管理器中,右键单击项目名称,选择"管理NuGet程序包"。然后,按照以下步骤安装所需的包:
- Microsoft.AspNet.WebApi.OwinSelfHost
- Microsoft.Owin.Cors
- Swashbuckle
- .。。。。
步骤3:创建API控制器 在解决方案资源管理器中,右键单击"Controllers"文件夹,选择"添加" -> "类"。命名为"模拟接口Controller.cs"。在类中添加以下代码:
using Newtonsoft.Json.Linq; using System.Threading.Tasks; using System.Web.Http; namespace MockAPI.Controllers { /// <summary> /// 模拟接口 /// </summary> [RoutePrefix("api")] public class 模拟接口Controller : BaseController { /// <summary> /// 同步信息 /// </summary> /// <returns></returns> [Route("fs_syncPayinfo")] [HttpGet] public IHttpActionResult SyncPayInfo() { string json = @"{""code"":1,""message"":""同步成功""}"; return Json(JObject.Parse(json)); } }
步骤4:配置Swagger 在解决方案资源管理器中,右键单击"Properties"文件夹,选择"添加" -> "新建文件"。命名为"Startup.cs"。在文件中添加以下代码:
其中包含了记录日志中间件等类,具体看下载的代码
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Text; using System.Web.Http; using System.Web.Http.Filters; using System.Xml; using Microsoft.Owin; using Microsoft.Owin.Cors; using MockAPI.Common; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Owin; using Swashbuckle.Application; using Swashbuckle.Swagger; [assembly: OwinStartup(typeof(MockAPI.Startup))] namespace MockAPI { public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); JsonSerializerSettings setting = new JsonSerializerSettings() { //日期类型默认格式化处理 DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, DateFormatString = "yyyy-MM-dd HH:mm:ss", //驼峰样式 ContractResolver = new CamelCasePropertyNamesContractResolver(), //空值处理 //NullValueHandling = NullValueHandling.Ignore, //设置序列化的最大层数 MaxDepth = 10, //解决json序列化时的循环引用问题 ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; config.Formatters.JsonFormatter.SerializerSettings = setting; config.Formatters.Remove(config.Formatters.XmlFormatter); config.Filters.Add(new HandlerErrorAttribute()); //config.Routes.MapHttpRoute( // name: "DefaultApi", // routeTemplate: "api/{controller}/{action}/{id}", // defaults: new // { // id = RouteParameter.Optional // } //); ConfigureSwagger(config); //添加路由路径 config.MapHttpAttributeRoutes(); app.UseCors(CorsOptions.AllowAll); app.Use<LoggingMiddleware>(); app.UseWebApi(config); } private static void ConfigureSwagger(HttpConfiguration config) { var thisAssembly = typeof(Startup).Assembly; config.EnableSwagger(c => { c.SingleApiVersion("v1", "MockAPI"); //设置接口描述xml路径地址 var webApiXmlPath = string.Format(string.Format("{0}/MockAPI.xml", AppDomain.CurrentDomain.BaseDirectory)); c.IncludeXmlComments(webApiXmlPath); c.UseFullTypeNameInSchemaIds(); //加入控制器描述 c.CustomProvider((defaultProvider) => new SwaggerControllerDescProvider(defaultProvider, webApiXmlPath)); }) .EnableSwaggerUi(c => { c.DocumentTitle("MockAPI"); c.InjectJavaScript(thisAssembly, "MockAPI.Common.Swagger.js"); }); } public class HandlerErrorAttribute : ExceptionFilterAttribute { /// <summary> /// 控制器方法中出现异常,会调用该方法捕获异常 /// </summary> /// <param name="context">提供使用</param> public override void OnException(HttpActionExecutedContext context) { base.OnException(context); LogFile.WriteError(context.Exception.Message); throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent( JsonConvert.SerializeObject( new { code = -1, data = "xxx", msg = context.Exception.Message }), Encoding.UTF8, "text/json") }); } }; public class SwaggerControllerDescProvider : ISwaggerProvider { private readonly ISwaggerProvider _swaggerProvider; private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>(); private readonly string _xml; /// <summary> /// /// </summary> /// <param name="swaggerProvider"></param> /// <param name="xml">xml文档路径</param> public SwaggerControllerDescProvider(ISwaggerProvider swaggerProvider, string xml) { _swaggerProvider = swaggerProvider; _xml = xml; } public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) { 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; } /// <summary> /// 从API文档中读取控制器描述 /// </summary> /// <returns>所有控制器描述</returns> public ConcurrentDictionary<string, string> GetControllerDesc() { string xmlpath = _xml; 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; } } } }
步骤5:配置OwinSelfHost 启动等等
private void Window_Loaded(object sender, RoutedEventArgs e) { setMin(); wsl = this.WindowState; this.Hide();//启动后直接最小化 this.ResizeMode = ResizeMode.CanMinimize; this.txtDevice.Text = DeviceNo; this.txtDevice.IsReadOnly = true; var registry = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);//检索指定的子项 if (registry != null) { object a = registry.GetValue(Path.GetFileName(System.Windows.Forms.Application.ExecutablePath)); if (a != null) this.cboAuto.IsChecked = true; registry.Close(); } this.cboAuto.Checked += BtnClickTurnOn; this.cboAuto.Unchecked += BtnClickTurnOff; StartOptions options = new StartOptions(); options.Urls.Add("http://+:8033"); // 启动 OWIN host _disposable = WebApp.Start<Startup>(options); }
这里代码只有部分截图,具体下载代码查看 点击下载
点击API地址打开文档界面
至此,我们已成功创建了一个使用WPF、OwinSelfHost和Swagger的自托管Web API。你可以根据自己的需求进一步扩展和定制这个应用程序。