首先來看一下經(jīng)典的Asp.net處理管道的生命周期。
成都創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于做網(wǎng)站、成都網(wǎng)站建設(shè)、固陽網(wǎng)絡(luò)推廣、重慶小程序開發(fā)公司、固陽網(wǎng)絡(luò)營銷、固陽企業(yè)策劃、固陽品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供固陽建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com
我們知道一個ASP.NET應(yīng)用程序可以有多個HttpModule,但是只能有一個HttpHandler,并且通過這個HttpHandler的BeginProce***equest(或Proce***equest)來處理并返回請求,查看聲明處理管道周期可知在MapHttpHandler這個周期將會根據(jù)請求的URL來查詢對應(yīng)的HttpHandler,那么它是如何查找的呢。
查找系統(tǒng)web.config中的httpModules配置節(jié),在倒數(shù)第二行發(fā)現(xiàn)一個name為UrlRoutingModule-4.0的IHttpModule配置,這是查找HttpHandler的關(guān)鍵之處。下面分析一下UrlRoutingModule的代碼:
protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] != null) { return; } application.Context.Items[_contextKey] = _contextKey; application.PostResolveRequestCache += OnApplicationPostResolveRequestCache; } private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; HttpContextBase context = new HttpContextWrapper(app.Context); PostResolveRequestCache(context); } public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = 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); }
可以看到UrlRoutingModule設(shè)置了一個PostResolveRequestCache事件處理方法,該方法從RouteCollection通過匹配找到請求對應(yīng)的路由數(shù)據(jù)RouteData(包含如Mvc中的Controller名、Action名等),然后從RouteData的屬性RouteHandler獲取一個IRouteHandler的實(shí)例,再從IRouteHandler實(shí)例里獲取對應(yīng)的IHttpHandler實(shí)例,最后調(diào)用HttpContext的RemapHandler方法重新為HttpContext設(shè)置RemapHandlerInstance。
根據(jù)前面asp.net初始化流程分析2我們知道在獲取Httphandler時(shí)經(jīng)典模式和集成模式使用了不同的IExecutionStep,經(jīng)典模式用的是MapHandlerExecutionStep集成模式用的是MaterializeHandlerExecutionStep,查看二者的執(zhí)行方法Execute。
先看MaterializeHandlerExecutionStep。
void IExecutionStep.Execute() { HttpContext context = _application.Context; HttpRequest request = context.Request; IHttpHandler handler = null; string configType = null; …… if (context.RemapHandlerInstance != null){ wr.SetScriptMapForRemapHandler(); context.Handler = context.RemapHandlerInstance; } …… }
可以看到MaterializeHandlerExecutionStep中如果UrlRoutingModule模塊中在HttpContext設(shè)置了RemapHandlerInstance,則直接用RemapHandlerInstance設(shè)置HttpContext的Handler。
再看MapHandlerExecutionStep。
void IExecutionStep.Execute() { HttpContext context = _application.Context; HttpRequest request = context.Request; …… context.Handler = _application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false /*useAppConfig*/); …… }
在MapHandlerExecutionStep中會調(diào)用HttpApplication的MapHttpHandler方法來設(shè)置HttpContext的Handler。下面查看MapHttpHandler代碼:
internal IHttpHandler MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, bool useAppConfig) { IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null; using (new ApplicationImpersonationContext()) { if (handler != null){ return handler; } …… }
從第一行代碼就可以看到如果HttpContext的RemapHandlerInstance不為空則直接返回HttpContext的RemapHandlerInstance(context.ServerExecuteDepth是指頁面是否使用了HttpServerUtility.Execute進(jìn)行頁面內(nèi)跳轉(zhuǎn))。這樣也就同樣使用了UrlRoutingModule模塊中在HttpContext設(shè)置的RemapHandlerInstance,至于HttpContext沒有設(shè)置的RemapHandlerInstance的情況下如何根據(jù)默認(rèn)的擴(kuò)展名匹配查找HttpHandler就不在此討論了。
通過上面的分析我們可以設(shè)想通過在UrlRoutingModule中的靜態(tài)RouteCollection屬性中注冊RouteData而且設(shè)置該RouteData的IRouteHandler(一個接口,只有一個方法GetHttpHandler用來獲取HttpHandler)來實(shí)現(xiàn)路由與HttpHandler的對應(yīng)。下面來分析一下MvcHandler是如何通過路由注冊的,首先來看RouteCollection的實(shí)現(xiàn)
public RouteCollection RouteCollection { get { if (_routeCollection == null) { _routeCollection = RouteTable.Routes; } return _routeCollection; } set { routeCollection = value; } }
可以看到RouteCollection其實(shí)是包裝了RouteTable中的靜態(tài)Routes,如果有Mvc項(xiàng)目經(jīng)驗(yàn)的話應(yīng)該很眼熟了,一般的Mvc程序在Global.asax中一般都有這么一段用來注冊路由:
protected void Application_Start() { …… RouteConfig.RegisterRoutes(RouteTable.Routes); …… } public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
而我們定義的路由設(shè)置MvcHandler的奧妙正在MapRoute方法里,這是一個擴(kuò)展方法,定義在System.Web.Mvc.RouteCollectionExtensions里:
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 = CreateRouteValueDictionaryUncached(defaults), Constraints = CreateRouteValueDictionaryUncached(constraints), DataTokens = new RouteValueDictionary() }; …… routes.Add(name, route); return route; }
可以看到MapRoute注冊路由是綁定了一個MvcRouteHandler作為IRouteHandler,下面看MvcRouteHandler是如何實(shí)現(xiàn)的:
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); return new MvcHandler(requestContext); }
在這里終于看到了創(chuàng)建MvcHandler的代碼。
至此,我們應(yīng)該有一個清晰的認(rèn)識了,我們通過全局靜態(tài)屬性集合(RouteTable.Routes)去添加各種各樣的Route(但應(yīng)該在HttpModule初始化周期之前,一般是利用HttpApplication創(chuàng)建的周期在Application_Start方法中添加了我們所需要的Route規(guī)則),當(dāng)然在添加路由的時(shí)候帶上了MvcHandler這個重要的HttpHandler。然后通過UrlRoutingModule在PostResolveRequestCache周期通過查找注冊的Route獲取請求的RouteData以及其屬性IRouteHandler實(shí)例(至于路由是如何匹配的還要等后續(xù)的篇章繼續(xù)講),然后通過IRouteHandler實(shí)例可以通過GetHttpHandler獲取IHttpHandler并將其設(shè)置到HttpContext的RemapHandlerInstance屬性。最終在MapHttpHandler周期通過獲取HttpContext的RemapHandlerInstance實(shí)現(xiàn)了不同的HttpHandler來接管匹配不同路由的URL。