學習是使用asp.net已經有很長一段時間了,現在就來分析一下mvc的整過過程吧。個人計划寫一個mvc系列的博文,僅從源代碼的角度來分析mvc。在接觸mvc時我們一定會經歷路由,那么路由這東東是怎么搞出來的啊。在我們的web.config中有這么一句: <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 看來路由是它咋負責了。在這個dll中有一個很特殊的類UrlRoutingModule
我們來看看它里面主要的核心代碼吧:
protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] == null) { application.Context.Items[_contextKey] = _contextKey; application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); } } private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); this.PostResolveRequestCache(context); } public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this.RouteCollection.GetRouteData(context); if (routeData != null) { IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (httpHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() })); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) { throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3")); } UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); } else { context.RemapHandler(httpHandler); } } } }
在IHttpModule.Init中注冊了一個PostResolveRequestCache事件,而該事件主要是調用PostResolveRequestCache這個方法,在這個方法里面有幾句很重要的代碼是
RouteData routeData = this.RouteCollection.GetRouteData(context);
IRouteHandler routeHandler = routeData.RouteHandler;
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
context.RemapHandler(httpHandler);
讓我們來分析第一句RouteData routeData = this.RouteCollection.GetRouteData(context) ,這句我們猜測是獲取路由信息。要想理解這句代碼又得回到我們程序中來,我們在Global.asax.cs文件中的RegisterRoutes方法中,默認有這么一句
routes.MapRoute(
"Default", // 路由名稱
"{controller}/{action}/{id}", // 帶有參數的 URL
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 參數默認值
);
這句代碼主要是注冊一個路由,這里的url要注意不能隨便寫,需要有controller和action。具體是怎么實現的了?
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }
各參數如下
routeName="Default", // 路由名稱
routeUrl= "{controller}/{action}/{id}", // 帶有參數的 URL
defaults=new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 參數默認值
constraints=null
namespaces=null
在這里創建了一個Route實例並且把它加入到RouteCollection中了。
如果我們的項目中有特殊的需要,需要創建自己的HttpHandler這么辦了?
其實很簡單只要我們注冊自己的IRouteHandler了,routes.Add(new Route("{controller}/{action}/{id}",new MvcRouteHandler())); 然后在里面GetHttpHandler實現自己的邏輯處理
現在又讓我們回到 RouteData routeData = this.RouteCollection.GetRouteData(context);這句代碼中來,GetRouteData的主要代碼如下:
public RouteData GetRouteData(HttpContextBase httpContext)
{
using (this.GetReadLock())
{
foreach (RouteBase base2 in this)
{
RouteData routeData = base2.GetRouteData(httpContext);
if (routeData != null)
{
return routeData;
}
}
}
return null;
}
在這里的base2就是我們先前調用MapRoute是添加的Route的。而Route的GetRouteData的方法如下:
public override RouteData GetRouteData(HttpContextBase httpContext) { string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults); if (values == null) { return null; } RouteData data = new RouteData(this, this.RouteHandler); if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { return null; } foreach (KeyValuePair<string, object> pair in values) { data.Values.Add(pair.Key, pair.Value); } if (this.DataTokens != null) { foreach (KeyValuePair<string, object> pair2 in this.DataTokens) { data.DataTokens[pair2.Key] = pair2.Value; } } return data; }
這個方法很復雜,有許多驗證和檢查,我們主要關心一句 RouteData data = new RouteData(this, this.RouteHandler);
當然剩下 RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;這2句沒什么特別了。
現在讓我們來看看IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);這句究竟干了些什么,意思很明白獲取Httphandler。
那么MvcRouteHandler是如何獲取一個Httphandler的了,
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
直接返回了一個MvcHandler實例。
最有一句context.RemapHandler(httpHandler); 很簡單很好明白吧,在HttpContext的RemapHandler方法中有這么一句 this._remapHandler = handler;
在HttpContext中有這個屬性
internal IHttpHandler RemapHandlerInstance
{
get
{
return this._remapHandler;
}
}
那么這個東西又是什么時候調用的了,在HttpApplication的內部類MaterializeHandlerExecutionStep中的 void HttpApplication.IExecutionStep.Execute()方法調用
if (httpContext.RemapHandlerInstance != null)
{
httpContext.Handler = httpContext.RemapHandlerInstance;
}
看到MaterializeHandlerExecutionStep這個了類名,我想大家都能猜到吧。在內部類PipelineStepManager中BuildSteps方法有
HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(app);
app.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);
我想大家看到這里對mvc整個路由應該有個大致的理解了吧。