您的位置:首页 > 其它

MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

2016-10-24 16:30 477 查看
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎。这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之处,还希望大家斧正,博主感激不尽!

本文原创地址:http://www.cnblogs.com/landeanfen/p/5989092.html

MVC源码学习系列文章目录:

MVC系列——MVC源码学习:打造自己的MVC框架(一)

MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

MVC系列——MVC源码学习:打造自己的MVC框架(四:自定义视图)

一、MVC原理解析

最近园子里Asp.Net Core火了一阵,不管微软的开源动作有多么迟缓,还是希望微软能够给力一次。作为Core的主要Web框架——MVC,虽然已经开源,但是读起来着实费劲,并且感觉很多核心部件都找不到。于是只能通过Reflector去反编译MVC5的组件以及参考博客园Fish Li等大神的文章去学习下MVC5的原理。

10月26日更新:感谢园友Adming在评论中提醒,原来Asp.net Core Mvc和Asp.net Mvc 5的原理已经完全不同,难怪在Core Mvc的源码里面已经找不到MvcHandler、UrlRoutingModule等核心部件了呢,此系列文章就先学习下MVC5的原理,等以后有空了再来研究Core MVC吧。

Asp.Net Core MVC的开源地址:https://github.com/aspnet/Mvc

Asp.net MVC的开源地址:http://aspnetwebstack.codeplex.com/SourceControl/latest

1、MVC原理

之前的文章有介绍MVC的路由机制,其实路由机制算是MVC的原理的核心之一。在此我们还是要不厌其烦再来谈谈整个过程,因为这是理解MVC原理不可逾越的鸿沟。当我们收到一个URL的请求时,服务端收到请求,主要经历以下几个步骤:

请求被UrlRoutingModule部件拦截

封装请求上下文HttpContext,成为HttpContextWrapper对象。

根据当前的HttpContext,从Routes集合中得到与当前请求URL相符合的RouteData对象。

RouteDataHttpContext请求封装成一个RequestContext对象。

根据RequestContext对象,从RouteData的RouteHandler中获取IHttpHandler(MVC里面会有一个IHttpHandler的实现类MvcHandler)。

执行IHttpHandler(MvcHandler),然后就是通过反射激活具体的controller,执行具体的action。

附上一张大致的流程图:

public class TestMyModule2:IHttpModule
{
public void Dispose()
{
//throw new NotImplementedException();
}

public void Init(HttpApplication app)
{
//事件注册
app.BeginRequest += app_BeginRequest;
app.EndRequest += app_EndRequest;
}

void app_EndRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.Response.Write("请求拦截结束");
}

void app_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.Response.Write("请求拦截开始");

//请求拦截的处理逻辑....
}
}


TestMyModule2.cs
最后在Web.config里面配置两个Module:

<system.webServer>
<modules>
<add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" preCondition="integratedMode" />
<add name="TestMyModule2" type="MyTestMVC.TestMyModule2, MyTestMVC" preCondition="integratedMode" />
</modules>
</system.webServer>


得到结果:



这说明同一个事件可以注册多次,即可以在同一个事件里面做不同的事。

3、HttpModule和HttpHandler如何区分

通过上文的HttpModule的应用,我们看到在Init方法里面可以拿到当前应用的HttpApplication对象,拿到这个貌似就可以拿到当前请求上下文里面的Request、Response了,是不是就可以处理当前的http请求了,从这点上来说,HttpModule也能处理http请求,或者说具有处理http请求的能力。既然HttpHandler和HttpModule都可以处理http请求,那在使用的时候如何区分呢?上文说过,HttpModule的作用类似AOP,是针对某些通用功能(请求拦截、身份认证、检查功能)的,而HttpHandler常用来处理某一类(ashx、aspx、asmx)http请求,两者的侧重点不同,至于具体在实际中如何使用,你可以自行考量。

4、UrlRoutingModule解析

好了,上面介绍那么多HttpModule的使用,都是在为了解Mvc里面的UrlRoutingModule做铺垫。上文说过UrlRoutingModule的作用是拦截请求,那么它是如何做的呢,还是来反编译看看吧。

public class UrlRoutingModule : IHttpModule
{
// Fields
private static readonly object _contextKey;
private static readonly object _requestDataKey;
private RouteCollection _routeCollection;

// Methods
static UrlRoutingModule();
public UrlRoutingModule();
protected virtual void Dispose();
protected virtual void Init(HttpApplication application);
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e);
[Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
public virtual void PostMapRequestHandler(HttpContextBase context);
public virtual void PostResolveRequestCache(HttpContextBase context);
void IHttpModule.Dispose();
void IHttpModule.Init(HttpApplication application);

// Properties
public RouteCollection RouteCollection { get; set; }
}


重点肯定在Init()方法。

图一:



注册HttpApplication对象的PostResolveRequestCache事件。

图二:



封装HttpContext,成为HttpContextWrapper对象

图三:



这部分代码是我们上述路由理论的代码实践,所以这段代码很重要,我们将代码拷贝出来:

     public virtual void PostResolveRequestCache(HttpContextBase context)
{
//1.传入当前上下文对象,得到与当前请求匹配的RouteData对象
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null)
{
//2.从RouteData对象里面得到当前的RouteHandler对象。其实这里的RouteHandler属性对应就是一个MvcRouteHandler的对象。
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
if (!(routeHandler is StopRoutingHandler))
{
//3.根据HttpContext和RouteData得到RequestContext对象
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;

//4.根据RequestContext对象得到处理当前请求的HttpHandler(MvcHandler)。
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
object[] args = new object[] { routeHandler.GetType() };
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), args));
}
if (httpHandler is UrlAuthFailureHandler)
{
if (!FormsAuthenticationModule.FormsAuthRequired) throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
}
else
//5.请求转到HttpHandler进行处理(进入到ProcessRequest方法)。这一步很重要,由这一步开始,请求才由UrlRoutingModule转到了MvcHandler里面
context.RemapHandler(httpHandler);
}
}
}


博主在主要的地方加上了注释。

代码释疑:这里有几点需要说明的。

1、HttpApplication对象的PostResolveRequestCache事件在MSDN上的解释是:在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。查阅相关资料发现,之所以在PostResolveRequestCache事件注册路由、匹配HttpHandler,是为了满足IIS6。可以参考Tom大叔的文章:http://www.cnblogs.com/TomXu/p/3756858.html

2、 IRouteHandler routeHandler = routeData.RouteHandler; 这里的routeHandler实际上是一个MvcRouteHandler类型的对象,为什么这么说,我们来反编译下这个就会一目了然:

图一:





MvcRouteHandler实现了IRouteHandler接口。然后我们重点来看GetHttpHandler()方法得到的是哪个HttpHandler。

图二:





看到最后一句是不是立马就明白了。也就是说GetHttpHandler()这个方法决定了采用MvcHandler去处理当前的http请求。所以在上述 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 这一句得到的就是一个MvcHandler的实例。

3、 context.RemapHandler(httpHandler); 这一句可以理解为将当前的请求上下文交给httpHandler这个对象去处理。

4、到这里,我们再反过来看前面的MVC的原理就完全明朗了。

请求被UrlRoutingModule部件拦截————通过注册HttpApplication对象的PostResolveRequestCache事件来实现拦截

封装请求上下文HttpContext,成为HttpContextWrapper对象。————将UrlRoutingModule的Init()方法转到定义,可以看到这么一句: HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);

根据当前的HttpContext,从Routes集合中得到与当前请求URL相符合的RouteData对象。————将UrlRoutingModule的Init()方法转到定义,最终会找到PostResolveRequestCache()方法,方法里面有一句 RouteData routeData = this.RouteCollection.GetRouteData(context);

RouteDataHttpContext请求封装成一个RequestContext对象。————同样在上述方法里面 RequestContext requestContext = new RequestContext(context, routeData);

根据RequestContext对象,从RouteData的RouteHandler中获取IHttpHandler(MVC里面会有一个IHttpHandler的实现类MvcHandler)。————同样在该方法里面 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

执行IHttpHandler(MvcHandler)。———— context.RemapHandler(httpHandler); 将请求交给MvcHandler处理。

然后就是通过反射激活具体的controller,执行具体的action。————在MvcHandler的ProcessRequest()方法里面的执行逻辑

四、总结

写到这里,总算把整个过程梳理了一遍,很多细节都未涉及,但是大的过程应该还是明朗的。通篇比较偏理论,所以整体上比较枯燥,但是还是希望园友们能够静下心来慢慢看,因为博主觉得这些对于理解MVC原理太重要!!!想想看,如果你也完全理解了这个过程,是不是都可以自己通过实现IHttphandler和IHttpModule去搭建一个简单的MVC框架了,不错,博主确实是这样打算的,这篇把理论搞清楚,下篇就是实现的细节了。其实写自己的MVC框架更多的在于学习MVC原理,希望自己能够坚持下去。如果你觉得本文能够帮助你,可以右边随意 打赏 博主,也可以 推荐 进行精神鼓励。你的支持是博主继续坚持的不懈动力。

本文原创出处:http://www.cnblogs.com/landeanfen/

欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐