ASP.NET MVC随想录——锋利的KATANA 短命女 2022-07-16 23:42 195阅读 0赞 # # 阅读目录 * [ASP.NET 发展历程][ASP.NET] * [走进Katana的世界][Katana] * [使用ASP.NET/IIS托管Katana-based应用程序][ASP.NET_IIS_Katana-based] * [使用自定义Host(self-host)托管Katana-based应用程序][Host_self-host_Katana-based] * [使用OwinHost.exe托管Katana-based应用程序][OwinHost.exe_Katana-based] * [几种指定启动项Startup的方法][Startup] * [启动项Startup的高级应用][Startup 1] * [小结][Link 1] > `正如上篇文章所述那样,OWIN在Web Server与Web Application之间定义了一套规范(Specs),意在解耦Web Server与Web Application, 从而推进跨平台的实现。若要真正使用OWIN规范,那么必须要对他们进行实现。目前有两个产品实现了OWIN规范——由微软主导的 Katana和第三方的Nowin。这篇文章,我主要关注还是Katana,由微软团队主导,开源到CodePlex上。 可以在Visual Studio中输入命令:git clone https://git01.codeplex.com/katanaproject来查看源代码。` `在介绍Katana之前,我觉得有必要为大家梳理一下过去10几年前ASP.NET 发展历程。` [回到顶部][Link 2] ### ASP.NET 发展历程 ### #### ASP.NET Web Form #### ASP.NET Web Form 在2002正式发布时,面向的开发者主要有两类: * 使用混合HTML标记和服务端脚本开发动态网站的ASP开发者,另外,ASP运行时抽象了底层的HTTP连接和Web Server,并为开发者提供了一系列的对象模型用于交互Http请求,当然也提供了额外的服务诸如Session、Cache、State等。 * 开发WinForm的程序员,他们可能对HTTP和HTML一无所知,但熟悉拖控件的方式来构建应用程序。 为了迎合这两类开发者,ASP.NET Web Form通过使用沉重的ViewState来保存页面回传过程中的状态值,因为HTTP协议是无状态的,通过ViewState,使原本没有记忆的Http协议变得有记忆起来。这在当时是非常好的设计,能通过拖拽控件的形式快速开发Web,而不必过多的去关注底层原理。同时ASP.NET团队还为ASP.NET丰富了更多的功能、诸如:Session、Cache、Configuration等等。 这在当时无疑是成功的,ASP.NET的发布迅速拉拢了开发者,在Web开发中形成了一股新的势力,但同时也买下来一些隐患: * 所有的功能、特性都发布在一个整体框架上并且紧耦合核心的Web抽象库——System.Web * System.Web是.NET Framework的重要组成部分,这意味着要修复更新System.Web必须更新.NET Framework,但.NET Framework是操作系统的基础,为了稳定性往往不会频繁更新。 * ASP.NET Framework (System.Web)紧耦合IIS * IIS只能运行在Windows系统 #### ASP.NET MVC #### 由于Web Form产生一大堆ViewState和客户端脚本,这对开发者来说慢慢变成一种累赘,因为我们只想产生纯净的HTML标记。所以开发者更想去主动控制而非被动产生额外HTML标记。 所以微软基于MVC设计模式推出了其重要的Web Framework——ASP.NET MVC Framework,通过Model-View-Control解耦了业务逻辑和表现逻辑,同时没有了服务器端控件,将页面的控制权完全交给了开发者。 为了快速更新迭代,通过Nuget来获取更新,故从.NET Framework中分离开了。 但唯一不足的是,ASP.NET MVC还是基于ASP.NET Framework(注:ASP.NET MVC 6已经不依赖System.Web),所以Web Application和Web Server依旧没有解耦。 #### ASP.NET Web API #### 随着时间的推移,一些问题开始暴露出来了,由于Web Server和Web Application紧耦合在一起,微软在开发独立、简单的Framework上越发捉襟见肘,这和其他平台下开源社区蓬勃发展形成鲜明对比,幸运的是,微软做出了改变,推出了独立的Web Framework ——ASP.NET Web API,他适用于移动互联网并可以快速通过Nuget安装,更为重要的是,他不依赖System.Web,也不依赖IIS,你可以Self-Host或者在其他Web Server部署。 #### Katana #### 随着Web API能够运行在自己的轻量级的宿主中,并且越来越多简单、模块化、专一的Framework问世,开发人员有时候不得不启动单独的进程来处理Web应用程序的各种组件(模块)、如静态文件、动态文件、Web API和Socket。为了避免进程扩散,所有的进程必须启动、停止并且独立进行管理。这时,我们需要一个公共的宿主进程来管理这些模块。 > 这就是OWIN诞生的原因,解耦成最小粒度的组件,然后这些标准化框架和组件可以很容易地插入到OWIN Pipeline中,从而对组件进行统一管理。而Katana正是OWIN的实现,为我们提供了丰富的Host和Server。 [回到顶部][Link 2] ### 走进Katana的世界 ### Katana作为OWIN的规范实现,除了实现Host和Server之外,还提供了一系列的API帮助开发应用程序,其中已经包括一些功能组件如身份验证(Authentication)、诊断(Diagnostics)、静态文件处理(Static Files)、ASP.NET Web API和SignalR的绑定等。 #### Katana的基本原则 #### * 可移植性:从HostàServeràMiddleware,每个Pipeline中的组件都是可替换的,并且第三方公司和开源项目的Framework都是可以在OWIN Server上运行,也就是说不受平台限制,从而实现跨平台。 * 模块化:每一个组件都必须保持足够独立性,通常只做一件事,以混合模块的形式来满足实际的开发需求 * 轻量和高效:因为每一个组件都是模块化开发,而且可以轻松的在Pipeline中插拔组件,实现高效开发 #### Katana 体系结构 #### Katana实现了OWIN的Layers,所以Katana的体系结构和OWIN一致,如下所示: ![090841317854511.png][] 1.)Host :宿主Host被OWIN规范定义在第一层(最底层),他的职责是管理底层的进程(启动、关闭)、初始化OWIN Pipeline、选择Server运行等。 Katana为我们提供了3中选择: * IIS / ASP.NET :使用IIS是最简单和向后兼容方式,在这种场景中OWIN Pipeline通过标准的HttpModule和HttpHandler启动。使用此Host你必须使用System.Web作为OWIN Server * Custom Host :如果你想要使用其他Server来替换掉System.Web,并且可以有更多的控制权,那么你可以选择创建一个自定义宿主,如使用Windows Service、控制台应用程序、Winform来承载Server。 * OwinHost :如果你对上面两种Host还不满意,那么最后一个选择是使用Katana提供的OwinHost.exe:他是一个命令行应用程序,运行在项目的根部,启动HttpListener Server并找到基于约束的Startup启动项。OwinHost提供了命令行选项来自定义他的行为,比如:手动指定Startup启动项或者使用其他Server(如果你不需要默认的HttpListener Server)。 2.)Server Host之后的Layer被称为Server,他负责打开套接字并监听Http请求,一旦请求到达,根据Http请求来构建符合OWIN规范的Environment Dictionary(环境字典)并将它发送到Pipeline中交由Middleware处理。Katana对OWIN Server的实现分为如下几类: * System.Web:如前所述那样,System.Web和IIS/ASP.NET Host两者彼此耦合,当你选择使用System.Web作为Server ,Katana System.Web Server把自己注册为HttpModule和HttpHandler并且处理发送给IIS的请求,最后将HttpRequest、HttpResponse对象映射为OWIN环境字典并将它发送至Pipeline中处理。 * HttpListener:这是OwinHost.exe和自定义Host默认的Server。 * WebListener:这是ASP.NET vNext默认的轻量级Server,他目前无法使用在Katana中 3)Middleware Middleware(中间件)位于Host、Server之后,用来处理Pipeline中的请求,Middleware可以理解为实现了OWIN应用程序委托AppFun的组件。 Middleware处理请求之后并可以交由下一个Pipeline中的Middleware组件处理,即链式处理请求,通过环境字典可以获取到所有的Http请求数据和自定义数据。Middleware可以是简单的Log组件,亦可以为复杂的大型Web Framework,诸如:ASP.NET Web API、Nancy、SignlR等,如下图所示:Pipeline中的Middleware用来处理请求: ![090841323169840.png][] 4.)Application 最后一层即为Application,是具体的代码实现,比如ASP.NET Web API、SignalR具体代码的实现。 现在,我想你应该了解了什么事Katana以及Katana的基本原则和体系结构,那么现在就是具体应用到实际当中去了。 [回到顶部][Link 2] ### 使用ASP.NET/IIS托管Katana-based应用程序 ### * Visual Studio创建Web Application * Install-Package Microsoft.Owin.Host.SystemWeb 添加Startup启动类 * ASP.NET/IIS作为Host * System.Web作为Server * 在Startup的Configuration方法中实现OWIN Pipeline处理逻辑,如下代码所示: 1. public class Startup 2. \{ 3. public void Configuration(IAppBuilder app) 4. \{ 5. app.Run(context => 6. \{ 7. context.Response.ContentType = "text/plain"; 8. return context.Response.WriteAsync("Hello World"); 9. \}); 10. \} 11. \} app.Run方法将一个接受IOwinContext对象最为输入参数并返回Task的Lambda表达式作为OWIN Pipeline的最后处理步骤,IOwinContext强类型对象是对Environment Dictionary的封装,然后异步输出"Hello World"字符串。 细心的你可能观察到,在Nuget安装Microsoft.Owin.Host.SystemWeb程序集时,默认安装了依赖项Microsoft.Owin程序集,正式它为我们提供了扩展方法Run和IOwinContext接口,当然我们也可以使用最原始的方式来输出"Hello World"字符串,即Owin程序集为我们提供的最原始方式,这仅仅是学习上参考,虽然我们不会在正式场景下使用: 1. using AppFunc = Func<IDictionary<string, object>, Task>; 2. public class Startup 3. \{ 4. public void Configuration(IAppBuilder app) 5. \{ 6. app.Use(new Func<AppFunc, AppFunc>(next => (env => 7. \{ 8. string text = "Hello World"; 9. var response = env\["owin.ResponseBody"\] as Stream; 10. var headers = env\["owin.ResponseHeaders"\] as IDictionary<string, string\[\]>; 11. headers\["Content-Type"\] = new\[\] \{ "text/plain" \}; 12. return response.WriteAsync(Encoding.UTF8.GetBytes(text), 0, text.Length); 13. \}))); 14. \} 15. \} [回到顶部][Link 2] ### 使用自定义Host(self-host)托管Katana-based应用程序 ### 使用自定义Host托管Katana应用程序与使用IIS托管差别不大,你可以使用控制台、WinForm、WPF等实现托管,但要记住,这会失去IIS带有的一些功能(SSL、Event Log、Diagnostics、Management…),当然这可以自己来实现。 * 创建控制台应用程序 * Install-Package Microsoft.Owin.SelfHost * 在Main方法中使用Startup配置项构建Pipeline并监听端口 1. static void Main(string\[\] args) 2. \{ 3. using (WebApp.Start<Startup>("http://localhost:10002")) 4. \{ 5. System.Console.WriteLine("启动站点:http://localhost:10002"); 6. System.Console.ReadLine(); 7. \} 8. \} 使用自定义的Host将失去IIS的一些功能,当然我们可以自己去实现。幸运的是,Katana为我们默认实现了部分功能,比如Diagnostic,包含在程序集Microsoft.Owin.Diagnostic中。 1. public void Configuration(IAppBuilder app) 2. \{ 3. app.UseWelcomePage("/"); 4. app.UseErrorPage(); 5. app.Run(context => 6. \{ 7. //将请求记录在控制台 8. Trace.WriteLine(context.Request.Uri); 9. //显示错误页 10. if (context.Request.Path.ToString().Equals("/error")) 11. \{ 12. throw new Exception("抛出异常"); 13. \} 14. context.Response.ContentType = "text/plain"; 15. return context.Response.WriteAsync("Hello World"); 16. \}); 17. \} 在上述代码中,当请求的路径(Request.Path)为根目录时,渲染输出Webcome Page并且不继续执行Pipeline中的其余Middleware组件,如下所示: ![090841333322784.png][] 如果请求的路径为Error时,抛出异常,显示错误页,如下所示: ![090841339264600.png][] [回到顶部][Link 2] ### 使用OwinHost.exe托管Katana-based应用程序 ### 当然我们还可以使用Katana提供的OwinHost.exe来托管应用程序,毫无疑问,通过Nuget来安装OwinHost。 如果你按照我的例子一步一步执行的话,你会发现不管使用ASP.NET/IIS托管还是自托管,Startup配置类都是不变的,改变的仅仅是托管方式。同理OwinHost也是一样的,但它更灵活,我们可以使用类库或者Web应用程序来作为Application。 * 使用类库 类库作为Application,可以最小的去引用程序集,创建一个类库后,删除默认的Class1.cs,然后并且添加Startup启动项,这会默认像类库中添加Owin和Microsoft.Owin程序集的引用。 然后,使用Nuget来安装OwinHost.exe,如Install-Package OwinHost,注意它并不是一个程序集,而是.exe应用程序位于<solution root>/packages/OwinHost.(version)/tools文件夹。 因为类库不能直接运行,那么只能在它的根目录调用OwinHost.exe来托管,它将加载.\\bin文件下所有的程序集,所以需要改变类库的默认输出,如下所示: ![090841344736700.png][] 然后编译解决方案,打开cmd,键入如下命令: ![090841357383186.png][] 如上图成功启动了宿主Host并且默认监听5000端口。 OwinHost.exe还提供自定义参数,通过追加-h来查看,如下所示: ![090841371912430.png][] 既然类库不能直接运行,当然你也不能直接进行调试,我们可以附加OwinHost进程来进行调试,如下所示: ![090841377546002.png][] > 注: > > 我在使用OwinHost.exe 3.0.1时,Startup如果是如下情况下,它提示转换失败,不知是否是该版本的Bug。 1. using AppFunc = Func<IDictionary<string, object>, Task>; 2. public class Startup 3. \{ 4. public void Configuration(IAppBuilder app) 5. \{ 6. //使用OwinHost.exe,报错,提示转换失败 7. app.Run(context=>context.Response.WriteAsync("Hello World")); 8. //使用OwinHost.exe 不报错 9. //app.Use(new Func<AppFunc, AppFunc>(next => (env => 10. //\{ 11. // string text = "Hello World"; 12. // var response = env\["owin.ResponseBody"\] as Stream; 13. // var headers = env\["owin.ResponseHeaders"\] as IDictionary<string, string\[\]>; 14. // headers\["Content-Type"\] = new\[\] \{ "text/plain" \}; 15. // return response.WriteAsync(Encoding.UTF8.GetBytes(text), 0, text.Length); 16. //\}))); 17. \} 18. \} 报错信息如下: ![090841381296817.png][] * 使用Web Application Web Application比类库使用起来轻松多了,你可以直接运行和调试,唯一比较弱的可能是它引用较多的程序集,你完全可以删掉,比如System.Web。 通过Nuget安装了OwinHost.exe之后,可以在Web中使用它,如下所示: ![090841385194403.png][] [回到顶部][Link 2] ### 几种指定启动项Startup的方法 ### * 默认名称约束:默认情况下Host会去查找root namespace下的名为Startup的类作为启动项。 * OwinStartup Attribute:当创建Owin Startup类时,自动会加上Attribute 如:\[assembly:OwinStartup(typeof(JKXY.KatanaDemo.OwinHost.Startup))\] * 配置文件,如: <addkey="owin:appStartup"value="JKXY.KatanaDemo.Web.StartupDefault" /> * 如果使用自定义Host,那么可以通过WebApp.Start<Startup>("http://localhost:10002") 来设置启动项。 * 如果使用OwinHost,那么可以通过命令行参数来实现,如下截图所示 ![090841392696031.png][] [回到顶部][Link 2] ### 启动项Startup的高级应用 ### 启动项Startup支持Friendly Name,通过Friendly Name我们可以动态切换Startup。 比如在部署时,我们会有UAT环境、Production环境,在不同的环境中我们可以动态切换Startup来执行不同的操作。 举个栗子,我创建来两个带有Friendly Name的Startup,如下所示: * Production Startup ![复制代码][copycode.gif] 1 [assembly: OwinStartup("Production", typeof(JKXY.KatanaDemo.Web.StartupProduction))] 2 namespace JKXY.KatanaDemo.Web 3 { 4 using AppFunc = Func<IDictionary<string, object>, Task>; 5 public class StartupProduction 6 { 7 public void Configuration(IAppBuilder app) 8 { 9 app.Run(context=>context.Response.WriteAsync("Production")); 10 } 11 } 12 } ![复制代码][copycode.gif] * UAT Startup ![复制代码][copycode.gif] 1 [assembly: OwinStartup("UAT",typeof(JKXY.KatanaDemo.Web.StartupUAT))] 2 3 namespace JKXY.KatanaDemo.Web 4 { 5 public class StartupUAT 6 { 7 public void Configuration(IAppBuilder app) 8 { 9 app.Run(context=>context.Response.WriteAsync("UAT")); 10 } 11 } 12 } ![复制代码][copycode.gif] * 根据Friendly Name使用配置文件或者OwinHost 参数来切换Startup <appSettings> <add key="owin:appStartup" value="Production" /> </appSettings> [ASP.NET]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label0 [Katana]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label1 [ASP.NET_IIS_Katana-based]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label2 [Host_self-host_Katana-based]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label3 [OwinHost.exe_Katana-based]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label4 [Startup]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label5 [Startup 1]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label6 [Link 1]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_label7 [Link 2]: http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html#_labelTop [090841317854511.png]: /images/20220717/f80176da99e746e3a5bc39ae3e7f5b7c.png [090841323169840.png]: /images/20220717/66bfe220a8184f178ac53da2d2a0c3f2.png [090841333322784.png]: /images/20220717/d5c155012a954ea59ac6bd015b5a34ca.png [090841339264600.png]: /images/20220717/882f3e455fd74c5b9ae1277ce3fc1b75.png [090841344736700.png]: /images/20220717/24800b0e17114636a4554c6f505eba57.png [090841357383186.png]: /images/20220717/f6b36f99273c47a29e8c5c475cc522a2.png [090841371912430.png]: /images/20220717/525e2075a96e4dbdb1498f1c1113429c.png [090841377546002.png]: /images/20220717/6b9ac838799d41f3adf2e37fd5fcaaa1.png [090841381296817.png]: /images/20220717/4a7f9aaae46c48ee8a0bdcae7e1b48c3.png [090841385194403.png]: /images/20220717/5819780a23b944fe8575104daab8d49d.png [090841392696031.png]: /images/20220717/3b52b6978e0f49f093e2eee62bf221ba.png [copycode.gif]: /images/20220717/69e81bb7888847e88d58eac34942e718.png
相关 代码随想录 前言 代码随想录算法训练营day44 -------------------- 一、Leetcode 518. 零钱兑换 II 1.题目 给你一个整数数组 布满荆棘的人生/ 2024年02月22日 09:45/ 0 赞/ 56 阅读
相关 代码随想录 前言 代码随想录算法训练营day14 -------------------- 一、Leetcode \\\\ 144. 二叉树的前序遍历 1.题目 给你 待我称王封你为后i/ 2024年02月22日 09:16/ 0 赞/ 45 阅读
相关 代码随想录 一、Leetcode 20. 有效的括号 1.题目 给定一个只包括 '(',')','\{','\}','\[','\]' 的字符串 s ,判断字符串是否有效。 ﹏ヽ暗。殇╰゛Y/ 2024年02月22日 09:15/ 0 赞/ 63 阅读
相关 代码随想录 前言 代码随想录算法训练营day06 -------------------- 一、哈希表基础知识 【 [代码随想录][Link 1]】 【[菜鸟教 桃扇骨/ 2024年02月22日 09:14/ 0 赞/ 49 阅读
相关 代码随想录 前言 代码随想录算法训练营day39 -------------------- 一、Leetcode 62.不同路径 1.题目 一个机器人位于一个 m x 女爷i/ 2024年02月22日 04:39/ 0 赞/ 82 阅读
相关 代码随想录 前言 代码随想录算法训练营day37 -------------------- 一、Leetcode 968.监控二叉树 v 1.题目 给定一个二叉树,我 朱雀/ 2024年02月22日 04:38/ 0 赞/ 109 阅读
相关 代码随想录 前言 代码随想录算法训练营day39 -------------------- 一、Leetcode 62.不同路径 1.题目 一个机器人位于一个 m x 左手的ㄟ右手/ 2024年02月22日 02:29/ 0 赞/ 52 阅读
相关 代码随想录 前言 代码随想录算法训练营day35 -------------------- 一、Leetcode 860.柠檬水找零 1.题目 在柠檬水摊上,每一杯柠 朱雀/ 2024年02月22日 02:26/ 0 赞/ 117 阅读
相关 代码随想录 前言 代码随想录算法训练营day34 -------------------- 一、Leetcode 1005.K次取反后最大化的数组和 1.题目 给你一 ゞ 浴缸里的玫瑰/ 2024年02月22日 02:25/ 0 赞/ 40 阅读
相关 ASP.NET MVC随想录——锋利的KATANA 阅读目录 [ASP.NET 发展历程][ASP.NET] [走进Katana的世界][Katana] [使用ASP.NET/IIS托管Kata 短命女/ 2022年07月16日 23:42/ 0 赞/ 196 阅读
还没有评论,来说两句吧...