- A+
这篇文章只是我学习Web API框架的输出,学习方法还是输出倒逼输入比较行得通,所以不管写的好不好,坚持下去,肯定有收获。篇幅比较长,仔细思考阅读下来大约需要几分钟。
做.NET开发有好几年时间了,从很久之前的WebForm到MVC,再到目前前后端分离模式下RESTful风格的 Web API ,相信这些Web框架很多人都或多或少的用过,也算见证了NET Web端的某一阶段的发展吧,同时很多技术随着发展和迭代,以及前后端分离模式的普及和兴起,用的机会少了,难免可能觉得已经过时了,同时现在流行的Web框架太多太多,不局限于.Net,同样也很优秀,例如:
Java的Spring | Hibernate
Python的Django | Flask
Node的Express和Koa
其实最重要的不是用了多少,知道多少,而是有多少沉淀,在使用领域虽然可能存在过时了,但是从技术的角度,框架设计的思想、代码风格,技术点的使用把控、甚至变量声明等等,都值得我们去学习。
分享方式
- Web API 路由注册和路由处理通过阅读源代码以及边说明的的方式来阐述。
- Web API 管道组装和扩展虽然我们在阅读源码时会接触到它,但我仍然会用
Demo
的方式来说明它.- Web API 管道组装和扩展我觉得它设计的真的很棒,忍不住要单独拎出来说一下,虽然有可能把自己讲懵,但我仍然想尝试一下。
1. Web API 路由简介
1.基本介绍
一个ASP.NET的Web应用具有一个全局的路由表,它是通过一个RouteTable类中的一个类型为RouteCollection
的Routes静态属性来表示的。
为什么这么说呢?或许这样说不太好理解,用一个简单的例子来说明,我们修改Api框架启动时,注册路由的方式,改为直接用 RouteTable.Routes来添加,这种做法跟框架提供的是一样的,最终都是将路由添加到一个地方。
在针对于路由表这一点上面不仅仅只有WebAPI是这么做的,他同样适用于WebForm 和MVC 同样可以直接在应用程序启动时网路由表中直接添加数据,不过此处只为了证实上述描述,在日常开发中是否可以这样用,完全取决于你自己。
1.框架提供的
public static void Register(HttpConfiguration config) { // Web API 框架提供的路由 config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{Action}/{id}", defaults: new { id = RouteParameter.Optional } ); }
2.通过直接路由表添加
//在WebApiConfig.Register中修改注册路由. public static void Register(HttpConfiguration config) { //验证路由是否加到全局路由表 var ro = new { id = RouteParameter.Optional }; //获取默认WebAPI路由处理器 IRouteHandler routeHandler = HttpControllerRouteHandler.Instance; RouteValueDictionary defaults = new RouteValueDictionary(ro); Route route = new Route("api/{controller}/{Action}/{id}", defaults, routeHandler); RouteTable.Routes.Add("DefaultApi", route); }
2.思考的问题
1.我们思考RouteCollection为什么是静态
的?
因为静态对象会一直存在内存中,直到程序池下一次回收之前.`
2.我们在用WebAPI开发应用接口的时候,前端Url请求是怎么通过路由到达我们对应的控制器和Action的?
其实是根据路由来控制的,至于怎么控制,怎么实现,后面会慢慢介绍.
3.路由控制如何路由呢?
1.先注册路由,再将路由和处理器绑定。
2.然后用户请求根据请求的Url 匹配对应的处理器,再由处理器进行路由模板规则解析。
3.根据解析到的规则反射找到Contrller和Action。
2. Web API 路由注册
路由注册这一部分在MVC5之后就有2部分了,新增了一个特性路由,此次不做分享,后续有机会再补上..我们看下简单的路由注册图,当然其中还有很多东西,只是画的比较简陋,
上面的所表示的流程中,我们作为使用框架的开发人员,关注的只是其中一小部分,从代码角度能看到,就只有如下很简单的几句代码,并且连这个代码都是框架生成的,从另一个角度也说明框架的封装比较完整和强大,让开发人员只关注自己的业务就行了
注册路由流程部分
1.注册方式
1.在Web Api程序启动时,首先调用GlobalConfiguration.Configure(WebApiConfig.Register)方法,这个方法接收的参数是一个Action<HttpConfiguration> 作为参数,看到Action我们的第一反应就是作为回调执行, 框架给我们预留了扩展空间,用户扩展的内容在内部执行
2.说白了GlobalConfiguration.Configure()方法的参数,需要一个委托,而委托的本质就是一个无返回值,包含HttpConfiguration类型的实例作为参数的方法,我们转到框架定义的WebApiConfig.Register()方法,毫无疑问它符合要求,所以这个方法就是在框架中被GlobalConfiguration.Configure() 执行的回调,而这个回调的作用就是注册路由。
注册路由作为框架GlobalConfiguration.Configure的回调,与上图中相等
2.GlobalConfiguration
我们发现更多的信息在GlobalConfiguration类中,继续一步步解读,打开源码找到GlobalConfiguration这个类来看
1.它是个静态类包含一个重要的方法Configure(Action
configurationCallback) 2.它包含3个重要的静态属性Configuration、DefaultHandler、DefaultServer
3.三个属性被调用时就已经初始化了,但被Lazy类型包裹,说明是被延迟执行,具体延迟执行的时机就是调用它的Value属性时。
4.目前在路由注册阶段只介绍Configuration,剩下2个在路由解析部分在分享.
1.我们首先看GlobalConfiguration.Configure(Action
执行回调需要的参数,正是第一个属性Configuration 通过该类型的Routes属性的MapHttpRoute扩展方法来往路由表中添加数据。可以知道我们只要搞清楚了HttpConfiguration类型的Configuration的来源,就能搞清楚很多事情。
我们找到HttpConfiguration类型的Configuration是怎么初始化的,先展开它,查看代码看到他是由内部的CreateConfiguration()方法创建的
3.注册流程步骤
1.在CreateConfiguration()中HttpConfiguration被构造时我们经过查看上下文发现
1.传入一个HostedHttpRouteCollection对象,并在内部赋值给HttpRouteCollection类型的_routes属性
2.HostedHttpRouteCollection在构造时,传入了我们的全局路由表RouteTable.Routes
2.我们找到映射路由的扩展方法MapHttpRoute可以知道我们调用它的Route属性是一个HostedHttpRouteCollection也就是说,在MapHttpRoute中CreateRoute()是由HostedHttpRouteCollection来调用的
3.继续转到HostedHttpRouteCollection类中的CreateRoute()内部看到它返回一个HostedHttpRoute而它就是实现IHttpRoute的实例。
4.继续深入在HostedHttpRoute构造
时,内部有一个Route类型的OriginalRoute属性,它被赋值为继承自(Route:RouteBase)的HttpWebRoute类型。
5.在HttpWebRoute初始构造时传入了几个极为重要
的参数路由模板和IRouteHandler类型的HttpControllerRouteHandler以及一个IHttpRoute类型的HostedHttpRoute,然后程序返回,注意此处的IRouteHandler可以理解为是路由的处理器.最终在HostedHttpRouteCollection对象上调用routes.Add(name, route)
添加 路由模板名字
和 处理程序
.
4.总结
现在我们对Web Api中的路由注册部分已经做了一个简单的介绍,并且一步一步理解了源码实现的逻辑,其实说到本质,路由注册只做了一件事,就是将路由模板规则和路由处理器提前绑定,客户端按照对应的规则请求
来匹配对应的路由处理器
做最终的处理,而目前框架内置的路由处理器就是一个HttpControllerRouteHandler
,后面部分进入路由处理,如果在阅读过程中有任何疑问欢迎随时和我讨论, 强烈建议在阅读本篇分享建立了粗略的知识点之后,有时间的话自己先下载Web Api源码进行阅读,这样可以帮助更好的理解。