ASP.NET 学习小记 -- “迷你”MVC实现(1)
ASP.NET 由于采用了管道式设计,具有很好的扩展性。整个ASP.NET MVC应用框架就是通过扩展ASP.NET实现的。通过ASP.NET的管道设计,我们知道,ASP.NET的扩展点主要是体现在HttpModule和HttpHandler这两个核心组件之上,实际上整个ASP.NET MVC的框架就是通过自定义的HttoModule和HttpHandler建立起来的。
当然,要从整体上把握ASP.NET MVC 的工作机制,我们可以通过查看其源码或自己实现一个“迷你版”的ASP.NET MVC 来了解其运行原理。
RouteData
ASP.NET定义了一个全局的路由表,路由表中的每个路由对象报刊一个URL模板。目标Controller和Action的名称可以通过路由变量以占位符(比如“{controller}”和“{action}”)定义在URL模板中,也可以作为路有对象的默认值。对于每一个抵达的HTTP请求,ASP.NET MVC会便利路由表找到一个具有与当前请求URL模式相匹配的路由对象,并最终解析出以Controller和Action名称为核心的路由数据。
1: public class RouteData
2: {
3: /// <summary>
4: /// 变量列表
5: /// </summary>
6: public IDictionary<string, object> Values { get; private set; }
7:
8: /// <summary>
9: /// 其他来源的变量列表
10: /// </summary>
11: public IDictionary<string, object> DataTokens { get; private set; }
12:
13: public IRouteHandler RouteHandler { get; set; }
14:
15: public RouteBase Route { get; set; }
16:
17: public RouteData()
18: {
19: this.Values = new Dictionary<string, object>();
20: this.DataTokens = new Dictionary<string, object>();
21: this.DataTokens.Add("namespaces", new List<string>());
22: }
23:
24: /// <summary>
25: /// 获取控制器名称
26: /// </summary>
27: public string Controller
28: {
29: get
30: {
31: object controllerName = string.Empty;
32: this.Values.TryGetValue("controller", out controllerName);
33: return controllerName.ToString();
34: }
35: }
36:
37: /// <summary>
38: /// 获取方法名称
39: /// </summary>
40: public string ActionName
41: {
42: get
43: {
44: object actionName = string.Empty;
45: this.Values.TryGetValue("action", out actionName);
46: return actionName.ToString();
47: }
48: }
49: }
RouteData的RouteHandler属性类型为IRouteHandler接口,该接口具有一个GetHttpHandler方法,用于返回真正用于处理HTTP请求的HttpHandler对象。
1: public interface IRouteHandler
2: {
3: IHttpHandler GetHttpHandler(RequestContext requestContext);
4: }
IRouteHandler接口的GetHttpHandler方法接受一个类型为RequestContext的参数,RequestContext表示当前HTTP请求的上下文,其核心就是对当前HttpContext和RouteData的封装。
1: public class RequestContext
2: {
3: public virtual HttpContextBase HttpContext { get; set; }
4:
5: public virtual RouteData RouteData { get; set; }
6: }
Route和RouteTable
RouteData具有一个类型为RouteBase的Route属性,该属性表示生成路由数据对应的路由对象。RouteBase是一个抽象类,它包含一个GetRouteData的方法,该方法用于判断是否与当前请求相匹配。并在匹配的情况下返回用于封装路由数据的RoutData对象。该方法接受一个表示当前HTTP上下文的HttpContextBase对象,如果与当前请求不匹配,则返回null。
1: public abstract class RouteBase
2: {
3: public abstract RouteData GetRouteData(HttpContextBase httpContext);
4: }
ASP.NET MVC 提供的基于URL模板的路由机制是通过其子类Route实现的。
1: public class Route : RouteBase
2: {
3:
4: public IRouteHandler RouteHandler { get; set; }
5:
6: public string Url { get; set; }
7:
8: public IDictionary<string, object> DataTokens { get; set; }
9:
10: public Route()
11: {
12: this.DataTokens = new Dictionary<string, object>();
13: this.RouteHandler = new MvcRouteHandler();
14: }
15:
16: public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
17: {
18: IDictionary<string, object> variables;
19: if(this.Match(httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2),out variables))
20: {
21: RouteData routeData = new RouteData();
22: foreach (var item in variables)
23: {
24: routeData.Values.Add(item.Key, item.Value);
25: }
26: routeData.RouteHandler = this.RouteHandler;
27: return routeData;
28: }
29: return null;
30: }
31:
32: public bool Match(string requestUrl, out IDictionary<string, object> variables)
33: {
34: variables = new Dictionary<string, object>();
35: string[] strArray1 = requestUrl.Split('/');
36: string[] strArray2 = this.Url.Split('/');
37: if (strArray1.Length != strArray2.Length)
38: {
39: return false;
40: }
41: for (int i = 0; i < strArray2.Length; i++)
42: {
43: if (strArray2[i].StartsWith("{") && strArray2[i].EndsWith("}"))
44: {
45: variables.Add(strArray2[i].Trim("{}".ToCharArray()), strArray1[i]);
46: }
47: else
48: {
49: if(string.Compare(strArray1[i],strArray2[i],true)!=0)
50: {
51: return false;
52: }
53: }
54: }
55: return true;
56: }
57: }
由于同一个Web应用可以采用多种不同的URL模式,所欲需要注册多个继承自RouteBase的路由对象,多个路由对象组成一个路由表。
1: public class RouteTable
2: {
3: public static RouteDictionary Routes { get; private set; }
4:
5: static RouteTable()
6: {
7: Routes = new RouteDictionary();
8: },
9: }
RouteDictionary表示一个具名的路由对象列表。这里我们让它继承自Dictionary<string,RouteBase>,其中key表示路由对象的注册名称。在System.Web.Routing中,它实际上是继承自RouteCollection对象。
1: public class RouteDictionary:Dictionary<string,RouteBase>
2: {
3: public RouteData GetRouteData(HttpContextBase httpContext)
4: {
5: foreach (var route in this.Values)
6: {
7: RouteData routeData = route.GetRouteData(httpContext);
8: if (routeData != null)
9: {
10: return routeData;
11: }
12: }
13: return null;
14: }
15: }
UrlRoutingModule
路由表的作用是对当前的HTTP请求的URL进行解析,从而获取一个以Controller和Action名称为核心的路由数据,即上面介绍的RouteData对象。整个解析过程是通过一个类型为UrlRoutingModule的自定义HttpModule来完成的。
1: public class UrlRoutingModule:IHttpModule
2: {
3: public void Dispose()
4: {
5:
6: }
7:
8: public void Init(HttpApplication context)
9: {
10: context.PostResolveRequestCache += OnPostResolveRequestCache;
11: }
12:
13: protected virtual void OnPostResolveRequestCache(object sender, EventArgs e)
14: {
15: HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
16: RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
17: if (routeData == null)
18: {
19: return;
20: }
21: RequestContext requestContext = new RequestContext
22: {
23: RouteData = routeData,
24: HttpContext = httpContext
25: };
26: IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext);
27: httpContext.RemapHandler(handler);
28: }
29: }
当PostResolveRequestCache事件触发之后,UrlRoutingModule通过RouteTable的静态子都属性Routes得到表示全局路由表的RouteDictionary对象,然后调用其GetRouteData方法并传入用于封装当前HttpContext的HttpContextWrapper对象,最终得到一个封装路由数据的RouteData对象。然后根据该对象和之前得到的HttpContextWrapper对象创建一个表示当前请求上下文的RequestContext对象并将其作为参数传入RouteData和RouteHandler的GetHttpHandler方法中,得到一个HttpContext对象,最后我们调用HttpContextWrapper对象的RemapHandler将Handler重新映射,使之用于对当前HTTP请求进行处理。
本学习内容和代码来自《ASP.NET MVC 4 框架揭秘》
ASP.NET 学习小记 -- “迷你”MVC实现(1)的更多相关文章
- ASP.NET 学习小记 -- “迷你”MVC实现(2)
Controller的激活 ASP.NET MVC的URL路由系统通过注册的路由表对HTTO请求进行解析从而得到一个用户封装路由数据的RouteData对象,而这个过程是通过自定义的UrlRoutin ...
- <转>ASP.NET学习笔记之MVC 3 数据验证 Model Validation 详解
MVC 3 数据验证 Model Validation 详解 再附加一些比较好的验证详解:(以下均为引用) 1.asp.net mvc3 的数据验证(一) - zhangkai2237 - 博客园 ...
- ASP.NET学习笔记1—— MVC
MVC项目文件夹说明 1.App_Data:用来保存数据文件 2.App_Start:包含ASP.NET-MVC系统启动的相关类文件 3.Controllers:存放整个项目"控制器&quo ...
- asp.net学习资料,mvc学习资料
http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/adding-validation-to-the-model
- asp.net学习资源汇总
名称:快速入门地址:http://chs.gotdotnet.com/quickstart/描述:本站点是微软.NET技术的快速入门网站,我们不必再安装.NET Framework中的快速入门示例程序 ...
- 迷你 MVC
深入研究 蒋金楠(Artech)老师的 MiniMvc(迷你 MVC),看看 MVC 内部到底是如何运行的 2014-04-05 13:52 by 自由的生活, 645 阅读, 2 评论, 收藏, 编 ...
- 深入研究 Mini ASP.NET Core(迷你 ASP.NET Core),看看 ASP.NET Core 内部到底是如何运行的
前言 几年前,Artech 老师写过一个 Mini MVC,用简单的代码告诉读者 ASP.NET MVC 内部到底是如何运行的.当时我研究完以后,受益匪浅,内心充满了对 Artech 老师的感激,然后 ...
- ASP.NET Core 2.0 MVC项目实战
一.前言 毕业后入职现在的公司快有一个月了,公司主要的产品用的是C/S架构,再加上自己现在还在学习维护很老的delphi项目,还是有很多不情愿的.之前实习时主要是做.NET的B/S架构的项目,主要还是 ...
- ASP.NETCore学习记录(一)
ASP.NETCore学习记录(一) asp.net core介绍 Startup.cs ConfigureServices Configure 0. ASP.NETCore 介绍 ASP.N ...
随机推荐
- Database ORM
Database ORM Introduction Basic Usage Mass Assignment Insert, Update, Delete Soft Deleting Timestamp ...
- XMl解析之Pull解析
HttpUtils: package cn.qf.parser; import java.io.BufferedOutputStream; import java.io.FileOutputStrea ...
- ElasticSearch 概念解析
索引(index) 1. 作为名词: 索引(index)指我们在 ES 中用来存放 doc 的一个库,类似数据库系统的 DB 概念. 2. 作为动词: 指把数据经过分析并存放到 ES 中形成索引(名词 ...
- Nim游戏(组合游戏Combinatorial Games)
http://baike.baidu.com/view/1101962.htm?fr=aladdin Nim游戏是博弈论中最经典的模型(之一),它又有着十分简单的规则和无比优美的结论 Nim游戏是组合 ...
- [转]Spring数据库读写分离
数据库的读写分离简单的说是把对数据库的读和写操作分开对应不同的数据库服务器,这样能有效地减轻数据库压力,也能减轻io压力. 主(master)数据库提供写操作,从(slave)数据库提供读操作,其实在 ...
- Nodejs v4.x.0API文档学习(2)Assert断言测试模块
文档参考地址:https://nodejs.org/dist/latest-v4.x/docs/api/ Assert(断言) assert模块提供了一组简单的断言测试方法,可以拥有测试不变量.该模块 ...
- 揭开CSS3媒体查询迷雾(min-width和max-width)
本文参考MichelleKlann的Media Queries Demystified: Min-Width and Max-Width 媒体查询(media queries)是响应式设计(Respo ...
- scala学习笔记:match与unapply()
编写如下代码: object MatchTest { def foo(a : Any) : String = { a match { case 1 => "int:1" ca ...
- 之前做web性能优化的一些个人心得
一个web项目后期的维护主要在于性能方面.数据吞吐量一旦增大各种bug都出来了.那些通过硬件<数据库分表,数据库主从分离,读写分离>等的一些手段此处就不多说了.本文主要在编码方面做一个性能 ...
- 服务端缓存HttpRuntime.Cache的使用
HttpRuntime.Cache.Insert("缓存key", "缓存content", null, DateTime.Now.AddMinutes(3), ...