zoukankan      html  css  js  c++  java
  • 进入MVC处理通道

    这一篇主要讲如何通过Asp.net处理管道把请求交给MVC进行处理的(进入MVC处理通道)。

    首先来看一下经典的Asp.net处理管道的生命周期。

     

    我们知道一个ASP.NET应用程序可以有多个HttpModule,但是只能有一个HttpHandler,并且通过这个HttpHandler的BeginProcessRequest(或ProcessRequest)来处理并返回请求,查看声明处理管道周期可知在MapHttpHandler这个周期将会根据请求的URL来查询对应的HttpHandler,那么它是如何查找的呢。

    查找系统web.config中的httpModules配置节,在倒数第二行发现一个name为UrlRoutingModule-4.0的IHttpModule配置,这是查找HttpHandler的关键之处。下面分析一下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设置了一个PostResolveRequestCache事件处理方法,该方法从RouteCollection通过匹配找到请求对应的路由数据RouteData(包含如Mvc中的Controller名、Action名等),然后从RouteData的属性RouteHandler获取一个IRouteHandler的实例,再从IRouteHandler实例里获取对应的IHttpHandler实例,最后调用HttpContext的RemapHandler方法重新为HttpContext设置RemapHandlerInstance。

    根据前面asp.net初始化流程分析2我们知道在获取Httphandler时经典模式和集成模式使用了不同的IExecutionStep,经典模式用的是MapHandlerExecutionStep集成模式用的是MaterializeHandlerExecutionStep,查看二者的执行方法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设置了RemapHandlerInstance,则直接用RemapHandlerInstance设置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中会调用HttpApplication的MapHttpHandler方法来设置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进行页面内跳转)。这样也就同样使用了UrlRoutingModule模块中在HttpContext设置的RemapHandlerInstance,至于HttpContext没有设置的RemapHandlerInstance的情况下如何根据默认的扩展名匹配查找HttpHandler就不在此讨论了。

    通过上面的分析我们可以设想通过在UrlRoutingModule中的静态RouteCollection属性中注册RouteData而且设置该RouteData的IRouteHandler(一个接口,只有一个方法GetHttpHandler用来获取HttpHandler)来实现路由与HttpHandler的对应。下面来分析一下MvcHandler是如何通过路由注册的,首先来看RouteCollection的实现

        public RouteCollection RouteCollection {
    
            get {
    
                if (_routeCollection == null) {
    
                    _routeCollection = RouteTable.Routes;
    
                }
    
                return _routeCollection;
    
            }
    
            set {
    
                routeCollection = value;
    
            }
    
    }

    可以看到RouteCollection其实是包装了RouteTable中的静态Routes,如果有Mvc项目经验的话应该很眼熟了,一般的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 }
                );
            }
        }

    而我们定义的路由设置MvcHandler的奥妙正在MapRoute方法里,这是一个扩展方法,定义在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是如何实现的:

        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
            return new MvcHandler(requestContext);
        }

    在这里终于看到了创建MvcHandler的代码。

    至此,我们应该有一个清晰的认识了,我们通过全局静态属性集合(RouteTable.Routes)去添加各种各样的Route(但应该在HttpModule初始化周期之前,一般是利用HttpApplication创建的周期在Application_Start方法中添加了我们所需要的Route规则),当然在添加路由的时候带上了MvcHandler这个重要的HttpHandler。然后通过UrlRoutingModule在PostResolveRequestCache周期通过查找注册的Route获取请求的RouteData以及其属性IRouteHandler实例(至于路由是如何匹配的还要等后续的篇章继续讲),然后通过IRouteHandler实例可以通过GetHttpHandler获取IHttpHandler并将其设置到HttpContext的RemapHandlerInstance属性。最终在MapHttpHandler周期通过获取HttpContext的RemapHandlerInstance实现了不同的HttpHandler来接管匹配不同路由的URL。

    至此对于进入MVC处理管道之前的一些处理基本讲完了,下面就正式进入MVC的旅程,同时分析的主要代码也进入了asp.net mvc项目。

  • 相关阅读:
    day3
    day2
    day1-存储
    day5-iptables
    MySQL之补充
    11.18
    11.17
    junit基础学习之-测试controller层(2)
    junit基础学习之-简介(1)
    外键和级联
  • 原文地址:https://www.cnblogs.com/ssxg/p/7091016.html
Copyright © 2011-2022 走看看