OWIN是Open Web Server Interface for .NET的首字母缩写,他的定义如下:
OWIN在.NET Web Servers与Web Application之间定义了一套标准接口,OWIN的目标是用于解耦Web Server和Web Application。基于此标准,鼓励开发者开发简单、灵活的模块,从而推进.NET Web Development开源生态系统的发展。
为什么我们需要OWIN
过去,IIS作为.NET开发者来说是最常用的Web Server(没有之一),源于微软产品的紧耦合关系,我们不得不将Website、Web Application、Web API等部署在IIS上,事实上在2010年前并没有什么不妥,但随着近些年来Web的发展,特别是移动互联网飞速发展,IIS作为Web Server已经暴露出他的不足了。主要体现在两个方面,ASP.NET (System.Web)紧耦合IIS,IIS紧耦合OS,这就意味着,我们的Web Framework必须部署在微软的操作系统上,难以跨平台。
...
OWIN是什么?在本文里面就不进行赘述,网上有很多介绍OWIN的信息以及优缺点的博文,这里可以给几个链接大家进行自行参考:
..
下面我们重点介绍我在搭建OWIN自宿主平台的过程,对于我是学习的过程,对于想要接触他的大家来说,也是一种帮助。
很多人搭建的OWIN+WebApi项目都是写在一个项目中的,我个人为了代码的隔离,将控制器层写在了另外一个项目中,这样有助于后期大型框架的形成。
下面是搭建步骤:
1、首先新建一个控制台应用程序和一个.NETFramework类库项目,控制台引用类库项目。
项目结构如下图所示:
OWIN.WebApi WebApi层
OWIN.WebApi.Sv WebApi服务层,将要作为启动项!
2、控制台项目使用NuGet引用需要的类库:
OWIN
Microsoft.Owin.Hosting
Microsoft.Owin.Host.HttpListener
Microsoct.AspNet.WebApi.Owin
这里需要手动从WebApi项目里面找到System.Web.Web,System.Net.Http等Web类库进行引用。
OWIN.WebApi.Srv层的引用情况(我这里有跨域配置,不需要的请忽略)
在OWIN.WebApi层,我们需要同样引用Web的类库,我们才可以在WebApi项目控制器层继承自ApiController
OWIN.WebApi层的引用情况(我这里有跨域配置,不需要的请忽略)
3、因为WebApi层要分开类库项目写,所以这里比一般的OWIN要多一些配置,在我项目的OWIN.WebApi层的config目录下,我新建了一个Global.cs类,里面的代码是对控制器的解析,代码展示如下:
using System.Web.Http; using System.Web.Http.Dispatcher; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http.Controllers; namespace OWIN.WebApi.config { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { //ignore the xml return it`s setting let json return only GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter); GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new WebApiControllerSelector(GlobalConfiguration.Configuration)); } } /// <summary> /// the WebApiControllerSelector /// author:qixiao /// time:2017-1-31 19:24:32 /// </summary> public class WebApiControllerSelector : DefaultHttpControllerSelector { private const string NamespaceRouteVariableName = "Namespace"; private readonly HttpConfiguration _configuration; private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerCache; public WebApiControllerSelector(HttpConfiguration configuration) : base(configuration) { _configuration = configuration; _apiControllerCache = new Lazy<ConcurrentDictionary<string, Type>>( new Func<ConcurrentDictionary<string, Type>>(InitializeApiControllerCache)); } private ConcurrentDictionary<string, Type> InitializeApiControllerCache() { IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver(); var types = this._configuration.Services.GetHttpControllerTypeResolver() .GetControllerTypes(assembliesResolver).ToDictionary(t => t.FullName, t => t); return new ConcurrentDictionary<string, Type>(types); } public IEnumerable<string> GetControllerFullName(HttpRequestMessage request, string controllerName) { object namespaceName; var data = request.GetRouteData(); IEnumerable<string> keys = _apiControllerCache.Value.ToDictionary<KeyValuePair<string, Type>, string, Type>(t => t.Key, t => t.Value, StringComparer.CurrentCultureIgnoreCase).Keys.ToList(); if (!data.Values.TryGetValue(NamespaceRouteVariableName, out namespaceName)) { return from k in keys where k.EndsWith(string.Format(".{0}{1}", controllerName, DefaultHttpControllerSelector.ControllerSuffix), StringComparison.CurrentCultureIgnoreCase) select k; } string[] namespaces = (string[])namespaceName; return from n in namespaces join k in keys on string.Format("{0}.{1}{2}", n, controllerName, DefaultHttpControllerSelector.ControllerSuffix).ToLower() equals k.ToLower() select k; } public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { Type type; if (request == null) { throw new ArgumentNullException("request"); } string controllerName = this.GetControllerName(request); if (string.IsNullOrEmpty(controllerName)) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("No route providing a controller name was found to match request URI '{0}'", new object[] { request.RequestUri }))); } IEnumerable<string> fullNames = GetControllerFullName(request, controllerName); ) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("No route providing a controller name was found to match request URI '{0}'", new object[] { request.RequestUri }))); } if (this._apiControllerCache.Value.TryGetValue(fullNames.First(), out type)) { return new HttpControllerDescriptor(_configuration, controllerName, type); } throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("No route providing a controller name was found to match request URI '{0}'", new object[] { request.RequestUri }))); } } }
4、在OWIN.WebApi.Srv层里面新建AppStart.cs类,并且写如下代码:
using Microsoft.Owin.Hosting; using System; using Owin; using System.Web.Http; using System.Web.Http.Dispatcher; using QX_Frame.App.WebApi.Extends; using System.Web.Http.Cors; namespace OWIN.WebApi.Srv { class AppStart { static void Main(string[] args) { //string baseAddress = "http://localhost:3999/"; //localhost visit string baseAddress = "http://+:3999/"; //all internet environment visit try { WebApp.Start<StartUp>(url: baseAddress); Console.WriteLine("BaseIpAddress is " + baseAddress); Console.WriteLine("\nApplication Started !"); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } for (;;) { Console.ReadLine(); } } } //the start up configuration class StartUp { public void Configuration(IAppBuilder appBuilder) { HttpConfiguration config = new HttpConfiguration(); // Web API configuration and services //跨域配置 //need reference from nuget config.EnableCors(new EnableCorsAttribute("*", "*", "*")); //enabing attribute routing config.MapHttpAttributeRoutes(); // Web API Convention-based routing. config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }, namespaces: new string[] { "OWIN.WebApi" } ); config.Services.Replace(typeof(IHttpControllerSelector), new OWIN.WebApi.config.WebApiControllerSelector(config)); //if config the global filter input there need not write the attributes //config.Filters.Add(new App.Web.Filters.ExceptionAttribute_DG()); //new ClassRegisters(); //register ioc menbers appBuilder.UseWebApi(config); } } }
里面对地址进行了配置,当然可以根据需求自行配置,显示信息也进行了适当的展示,需要说明的一点是,我这里进行了跨域的配置,没有配置或者是不需要的请注释掉并忽略!
这里需要注意的是第53行,这里引用的是刚才的OWIN.WebApi层的Global.cs里面的类,请对照上述两段代码进行查找。
这行是关键,有了这行,程序才可以扫描到WebApi层的Controller。好了,我们进行Controller的书写。
5、在OWIN.WebApi层进行控制器类的编写,这里随意,我只在这里列出我的例子。
using QX_Frame.App.WebApi; using QX_Frame.Helper_DG; using System.Web.Http; namespace OWIN.WebApi { /* * author:qixiao * time:2017-2-27 10:32:57 **/ public class Test1Controller:ApiController { //access http://localhost:3999/api/Test1 get method public IHttpActionResult GetTest() { //throw new Exception_DG("login id , pwd", "argumets can not be null", 11111, 2222); return Json(new { IsSuccess = true, Msg = "this is get method" }); } //access http://localhost:3999/api/Test1 post method public IHttpActionResult PostTest(dynamic queryData) { return Json(new { IsSuccess = true, Msg = "this is post method",Data=queryData }); } //access http://localhost:3999/api/Test1 put method public IHttpActionResult PutTest() { return Json(new { IsSuccess = true, Msg = "this is put method" }); } //access http://localhost:3999/api/Test1 delete method public IHttpActionResult DeleteTest() { return Json(new { IsSuccess = true, Msg = "this is delete method" }); } } }
这里我是用的是RESTFull风格的WebApi控制器接口。
然后我们可以进行试运行:
服务启动成功!
测试通过,我们可以尽情地探索后续开发步骤!