• 003. Asp.Net Routing与MVC 之一: 请求如何到达MVC


    基础知识

    本文用到的基础知识:URL、HttpModule 与 HttpHandler、IIS 的请求处理过程。

    URL

    HttpModule与HttpHandler

    IIS7.0的请求处理过程

     

    OK,现在我们来看请求如何到达MVC:

    一、Asp.Net Routing 如何起作用

    我们知道IIS网站的配置可以分为两个块:全局 Web.Config 和本站 Web.Config 。

    Asp.Net Routing属于全局性的,所以它配置在全局Web.Config 中,我们可以在如下路径中找到:

    “$WindowsMicrosoft.NETFramework版本号ConfigWeb.config“

       1: <?xml version="1.0" encoding="utf-8"?>
       2: <!-- the root web configuration file -->
       3: <configuration>
       4:     <system.web>
       5:         <httpModules>
       6:             <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
       7:         </httpModules>
       8:     </system.web>
       9: </configuration>

    通过在全局Web.Config中注册 System.Web.Routing.UrlRoutingModule,IIS请求处理管道接到请求后,就会加载 UrlRoutingModule类型的Init()方法。其源码入下:

       1: //UrlRoutingModule 位于 System.web.dll 文件中,利用Reflector 可以查看到其源码
       2: [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
       3: public class UrlRoutingModule : IHttpModule
       4: {
       5:     // Fields
       6:     private static readonly object _contextKey = new object();
       7:     private static readonly object _requestDataKey = new object();
       8:     private RouteCollection _routeCollection;
       9:     // Methods
      10:     protected virtual void Dispose()  {}
      11:  
      12:     //在II7处理管道中注册了的IHttpModule类型,其_Init()会被执行,以在II7处理管道中注册事件处理方法。
      13:     protected virtual void Init(HttpApplication application)
      14:     {
      15:         if (application.Context.Items[_contextKey] == null)
      16:         {
      17:             application.Context.Items[_contextKey] = _contextKey;
      18:             //这里为UrlRoutingModule 注册了一个PostResolveRequestCache 事件处理方法:OnApplicationPostResolveRequestCache().
      19:             application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
      20:         }
      21:     }
      22:  
      23:     //发生PostResolveRequestCache 事件时,该方法被调用
      24:     private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
      25:     {
      26:         HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
      27:         //执行真正的处理 PostResolveRequestCache()
      28:         this.PostResolveRequestCache(context);
      29:     }
      30:  
      31:     [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
      32:     public virtual void PostMapRequestHandler(HttpContextBase context)
      33:     {
      34:     }
      35:  
      36:      //发生PostResolveRequestCache 事件时真正的处理
      37:     public virtual void PostResolveRequestCache(HttpContextBase context)
      38:     {
      39:         //获取路由信息(RouteCollection是其本身的属性,见this.RouteCollection)
      40:         RouteData routeData = this.RouteCollection.GetRouteData(context);
      41:         if (routeData != null)
      42:         {
      43:             //从routeData 获取 RouteHandler
      44:             // 既然routeData 内容其实来自System.Web.Routing.RouteTable.Routes
      45:             // 那么System.Web.Routing.RouteTable.Routes 里的RouteHandler到底是哪来的呢?我们需要去MVC项目里看看
      46:             IRouteHandler routeHandler = routeData.RouteHandler;
      47:             if (routeHandler == null)
      48:             {
      49:                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, 
      50:                                 SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
      51:             }
      52:             if (!(routeHandler is StopRoutingHandler))
      53:             {
      54:                 //构建请求上下文
      55:                 RequestContext requestContext = new RequestContext(context, routeData);
      56:                 context.Request.RequestContext = requestContext;
      57:                 //调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例 
      58:                 //IHttpHandler 类型实例 ,是由 IRouteHandler.GetHttpHandler获取的,我们去MVC源码里看
      59:                 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
      60:                 if (httpHandler == null)
      61:                 {
      62:                     throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, 
      63:                                     SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
      64:                 }
      65:                 if (httpHandler is UrlAuthFailureHandler)
      66:                 {
      67:                     if (!FormsAuthenticationModule.FormsAuthRequired)
      68:                     {
      69:                         throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
      70:                     }
      71:                     UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
      72:                 }
      73:                 else
      74:                 {
      75:                     //合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中
      76:                     context.RemapHandler(httpHandler);
      77:                 }
      78:             }
      79:         }
      80:     }
      81:  
      82:     void IHttpModule.Dispose()
      83:     {
      84:         this.Dispose();
      85:     }
      86:  
      87:     void IHttpModule.Init(HttpApplication application)
      88:     {
      89:         this.Init(application);
      90:     }
      91:  
      92:     // Properties
      93:     public RouteCollection RouteCollection
      94:     {
      95:         get
      96:         {
      97:             //恩,原来真实内容来自System.Web.Routing.RouteTable.Routes
      98:             if (this._routeCollection == null)
      99:             {
     100:                 this._routeCollection = RouteTable.Routes;
     101:             }
     102:             return this._routeCollection;
     103:         }
     104:         set
     105:         {
     106:             this._routeCollection = value;
     107:         }
     108:     }
     109: }

    下边是PostResolveRequestCache方法里的几句核心代码:

       1: //PostResolveRequestCache方法里的核心代码:
       2:  
       3: //获取路由信息(RouteCollection是其本身的属性,见this.RouteCollection)
       4: RouteData routeData = this.RouteCollection.GetRouteData(context);
       5: //从routeData 获取 RouteHandler
       6: // 既然routeData 内容其实来自System.Web.Routing.RouteTable.Routes
       7: // 那么System.Web.Routing.RouteTable.Routes 里的RouteHandler到底是哪来的呢?我们需要去MVC项目里看看
       8: IRouteHandler routeHandler = routeData.RouteHandler;
       9:  
      10: //调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例 
      11: //IHttpHandler 类型实例 ,是由 IRouteHandler.GetHttpHandler获取的,我们去MVC源码里看
      12: IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
      13:  
      14: //合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中
      15: context.RemapHandler(httpHandler);

    二、MVCRouteHandler从哪来

    好吧,我们需要去MVC项目里看看。众所周知,项目启动是从Global开始的,那就看看它。下边是代码:

       1: //这是一个普通MVC5 WebApp的Global.asax.cs
       2: namespace WebApplication1
       3: {
       4:     public class MvcApplication : System.Web.HttpApplication
       5:     {
       6:         protected void Application_Start()
       7:         {
       8:             AreaRegistration.RegisterAllAreas();
       9:             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      10:             //这里要注册路由了
      11:             RouteConfig.RegisterRoutes(RouteTable.Routes);
      12:             BundleConfig.RegisterBundles(BundleTable.Bundles);
      13:         }
      14:     }
      15:  
      16:     //为方便起见,我把项目App_Start/RouteConfig.cs内容放在一起
      17:     public class RouteConfig
      18:     {
      19:         public static void RegisterRoutes(RouteCollection routes)
      20:         {
      21:             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
      22:             //玄机就在这了,这个MapRoute位于System.Web.Mvc.RouteCollectionExtensions
      23:             //看RouteCollectionExtensions里面做了什么
      24:             routes.MapRoute(
      25:                 name: "Default",
      26:                 url: "{controller}/{action}/{id}",
      27:                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
      28:             );
      29:         }
      30:     }
      31: }

    下边是System.Web.Mvc.RouteCollectionExtensions.MapRoute 众多重载的最终执行者代码:

       1: //System.Web.Mvc.RouteCollectionExtensions.MapRoute 众多重载的最终执行者代码
       2: [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
       3: public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
       4: {
       5:     if (routes == null)
       6:     {
       7:         throw new ArgumentNullException("routes");
       8:     }
       9:     if (url == null)
      10:     {
      11:         throw new ArgumentNullException("url");
      12:     }
      13:  
      14:     //终于找到了,“new MvcRouteHandler()”,
      15:     //直接把一个 MvcRouteHandler 实例塞到 System.Web.Routing 的初始化方法里了!
      16:     Route route = new Route(url, new MvcRouteHandler())
      17:     {
      18:         Defaults = CreateRouteValueDictionaryUncached(defaults),
      19:         Constraints = CreateRouteValueDictionaryUncached(constraints),
      20:         DataTokens = new RouteValueDictionary()
      21:     };
      22:  
      23:     ConstraintValidation.Validate(route);
      24:  
      25:     if ((namespaces != null) && (namespaces.Length > 0))
      26:     {
      27:         route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces;
      28:     }
      29:  
      30:     routes.Add(name, route);
      31:  
      32:     return route;
      33: }

    三、请求到达真正的处理程序:MVCHandler

    IRouteHandler 里只有一个方法 IHttpHandler GetHttpHandler();

       1: namespace System.Web.Routing
       2: {
       3:     // 摘要: 
       4:     //     定义类必须实现才能处理匹配路由模式的请求的协定。
       5:     [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
       6:     public interface IRouteHandler
       7:     {
       8:         // 摘要: 
       9:         //     提供处理请求的对象。//
      10:         // 参数: 
      11:         //   requestContext:
      12:         //     一个对象,封装有关请求的信息。
      13:         // 返回结果: 
      14:         //     一个处理请求的对象。
      15:         IHttpHandler GetHttpHandler(RequestContext requestContext);
      16:     }
      17: }

    MvcRouteHandler 是 IRouteHandler的实现,它实现的GetHttpHandler如下:

       1: //IRouteHandler.IRouteHandler() 的实现如下
       2:         protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
       3:         {
       4:             requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
       5:             //返回一个 MvcHandler的实例,至此,当IIS请求处理管道,执行熬 Handler.Exe 时,请求真正达到MVC
       6:             return new MvcHandler(requestContext);
       7:         }

    终于找到了!

    综上所述,一个请求从 IIS 到达 MvcRouteHandler 的顺序如下:

    1、IIS接到请求:

    1.1、IIS接到一个请求,检查请求的应用是否已初始化;
    1.2、若应用未初始化,为应用分配应用池程序资源,及其他应用程序域信息,加载全局和本站web.config信息以设定配置;
    1.3、开始应用初始化;

    2、开始应用初始化:

    2.1、在应用程序域,为应用程序创建环境对象(HostingEnvironment??)和响应对象(HttpContext、HttpRequest 和 HttpResponse);
    2.2、创建应用的 HttpApplication 类型实例(即Global.asax.cs 实例),以启动应用;
    2.3、在 Global.asax.cs 中,调用程序 RegisterRoutes 注册路由;
    2.4、在 RegisterRoutes 中,调用 System.Web.Mvc.RouteCollectionExtensions.MapRoute() 逐条注册;
    2.5、在 MapRoute中,直接把 MvcRouteHandler 类型实例塞到 System.Web.Routing 的初始化方法里,以填充 System.Web.Routing.RouteTable.Routes 数据;

    3、初始化完成,处理请求:

    3.1、HttpApplication 启动HTTP管道模型开始处理请求;
    3.2、HTTP 管道处理已注册的 IHttpModule 事件:System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache();
    3.3、随即 OnApplicationPostResolveRequestCache 调用 PostResolveRequestCache();
    3.4、PostResolveRequestCache 方法从 System.Web.Routing.RouteTable.Routes 的数据中获取 IHttpHandler 类型;
    3.5、在2.4中被直接塞入的 MvcRouteHandler 类型实例,被映射到 IIS HTTP处理管道中;
    3.6、最终,IIS HTTP处理管道调用 MvcHandler 处理请求,并返回 Response 内容;

    至此,请求 成功透过 Asp.Net 到达 MvcHandler 处理程序。

    系列一共计划三篇

    Asp.Net Routing与MVC 之一: 请求如何到达MVC

    Asp.Net Routing与MVC 之二: 请求如何激活Controller和Action

    Asp.Net Routing与MVC 之三: 路由在MVC的使用

    参考了一堆文章,下边是传送门

    ----------------------------------------

     http://msdn.microsoft.com/zh-cn/library/bb470252%28v=vs.100%29.aspx 

     http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture 

     http://blog.csdn.net/darren__chan/article/details/8215646

     http://www.cnblogs.com/isdavid/archive/2013/05/28/3103228.html

     http://www.cnblogs.com/fsjohnhuang/articles/2332074.html

     http://www.bdqn.cn/news/201309/11384.shtml

    ----------------------------------------

  • 相关阅读:
    Web框架下安全漏洞的测试反思
    如何能低成本地快速获取大量目标用户,而不是与竞争对手持久战?
    Spring-Boot自定义Starter实践
    WM_QUERYENDSESSION与WM_ENDSESSION
    AutoMapper用法
    使用AutoMapper实现Dto和Model的自由转换(下)
    使用AutoMapper实现Dto和Model的自由转换(中)
    使用AutoMapper实现Dto和Model的自由转换(上)
    JSON中JObject和JArray的修改
    通信对象 System.ServiceModel.Channels.ServiceChannel 无法用于通信,因为其处于“出错”状态。
  • 原文地址:https://www.cnblogs.com/acejason/p/3869731.html
Copyright © 2020-2023  润新知