• ASP.NET Web API 框架研究 ASP.NET 路由


      ASP.NET Web API 如果采用Web Host方式来寄宿,在请求进入Web API 消息处理管道之前,就会用ASP.NET 自身的路由系统根据注册的路由表,解析出当前请求的HttpController和Action的名称,以及与目标Action方法某个参数进行绑定的路由变量。

      ASP.NET路由系统包括两方面应用:

    • 注册路由映射,即注册路由模板和物理文件的匹配关系,实现请求URL地址和处理请求的物理地址的分离,可以提高请求URL可读性,SEO优化,灵活性,即请求URL和处理请求的物理文件的变化互不影响
    • URL生成,根据注册的路由规则生成相应URL,使用这种URL,刚好利用了路由注册的灵活性,可以使原来生成的URL不中断,只需要修改路由配置的处理文件。

    一、涉及的类及源码分析

      涉及的主要类型都在System.Web.Routing中,类及主要成员和相互关系如下图:

       

    1、RouteBase

      public abstract class RouteBase
      {
        private bool _routeExistingFiles = true;
        //根据路由模板与请求的URL进行匹配,如果成功返回RouteData,否则返回NULL
        public abstract RouteData GetRouteData(HttpContextBase httpContext);
        //采用指定的路由变量列表(包含名称和值)与URL路由模板进行匹配,若匹配成功,返回完整URL,否则返回NULL
        public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
        //表示是否对现有的物理文件实施路由,默认值为true,即通过地址“/employees/hr/index.aspx” 是访问不到 Index.aspx页面
        //但是有时候我们希望以物理文件路径方式来访问对应的物理文件,可以设置该值为false,就可以访问到Index.aspx页面
        public bool RouteExistingFiles
        {
          get
          {
            return this._routeExistingFiles;
          }
          set
          {
            this._routeExistingFiles = value;
          }
        }
      }

    2、Route

      RouteBase唯一子类,基于路由模板模式的路由匹配规则就定义在其中,向全局路由表中添加的就是一个Route对象。

      public class Route : RouteBase
      {
        private const string HttpMethodParameterName = "httpMethod";
        private string _url;
        private ParsedRoute _parsedRoute;

        //构造函数,前边省略N个重载
        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
        {
          this.Url = url;
          this.Defaults = defaults;
          this.Constraints = constraints;
          this.DataTokens = dataTokens;
          this.RouteHandler = routeHandler;
        }

        //为路由模板中的变量以正则表达式的形武设定一些约束条件,Key为变量名,Value为正则表达式
        //如果有定义,匹配也要满足该约束
        public RouteValueDictionary Constraints { get; set; }
        //存储额外的变量值
        public RouteValueDictionary DataTokens { get; set; }
        //路由变量默认值,也不一定要定义在路由模板中
        public RouteValueDictionary Defaults { get; set; }

        public IRouteHandler RouteHandler { get; set; }

        //路由模板,如/weather/{areacode}/{days},请求的URL就是跟该模板进行匹配,/分割成多个段,每个段又分变量(areacode,days)和字面量(weather)
        //匹配的两个条件,段数和路由模板相同,以及对应文本段内容也要相同,注,URL大小写不敏感
        public string Url
        {
          get
          {
            return this._url ?? string.Empty;
          }
          set
          {
            this._parsedRoute = RouteParser.Parse(value);
            this._url = value;
          }
        }

        //根据路由模板与请求的URL进行匹配,如果成功返回RouteData,否则返回NULL

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
          RouteValueDictionary values = this._parsedRoute.Match(httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo,         this.Defaults);
          if (values == null)
            return (RouteData)null;
          RouteData routeData = new RouteData((RouteBase)this, this.RouteHandler);
          if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
            return (RouteData)null;
          foreach (KeyValuePair<string, object> keyValuePair in values)
            routeData.Values.Add(keyValuePair.Key, keyValuePair.Value);
          if (this.DataTokens != null)
          {
            foreach (KeyValuePair<string, object> dataToken in this.DataTokens)
            routeData.DataTokens[dataToken.Key] = dataToken.Value;
          }
          return routeData;
        }

        //采用指定的路由变量列表(包含名称和值)与URL路由模板进行匹配,若匹配成功,返回完整URL,否则返回NULL
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
          BoundUrl boundUrl = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
          if (boundUrl == null)
            return (VirtualPathData)null;
          if (!this.ProcessConstraints(requestContext.HttpContext, boundUrl.Values, RouteDirection.UrlGeneration))
            return (VirtualPathData)null;
          VirtualPathData virtualPathData = new VirtualPathData((RouteBase)this, boundUrl.Url);
          if (this.DataTokens != null)
          {
            foreach (KeyValuePair<string, object> dataToken in this.DataTokens)
              virtualPathData.DataTokens[dataToken.Key] = dataToken.Value;
          }
          return virtualPathData;
        }

        //处理约束
        protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection       routeDirection)
        {
          IRouteConstraint routeConstraint = constraint as IRouteConstraint;
          if (routeConstraint != null)
            return routeConstraint.Match(httpContext, this, parameterName, values, routeDirection);
          string str = constraint as string;
          if (str == null)
            throw new InvalidOperationException(string.Format((IFormatProvider)CultureInfo.CurrentUICulture,                             System.Web.SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[2]
          {
            (object) parameterName,
            (object) this.Url
          }));
          object obj;
          values.TryGetValue(parameterName, out obj);
          return Regex.IsMatch(Convert.ToString(obj, (IFormatProvider)CultureInfo.InvariantCulture), "^(" + str + ")$", RegexOptions.IgnoreCase |               RegexOptions.CultureInvariant);
        }

        private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection)
        {
          if (this.Constraints != null)
          {
            foreach (KeyValuePair<string, object> constraint in this.Constraints)
            {
              if (!this.ProcessConstraint(httpContext, constraint.Value, constraint.Key, values, routeDirection))
              return false;
            }
          }
          return true;
        }

      }

      另外,RequestContext是对Http请求上下文和路由数据的封装

      public class RequestContext
      {
        public RequestContext()
        {
        }

        public RequestContext(HttpContextBase httpContext, RouteData routeData)
        {
          if (httpContext == null)
            throw new ArgumentNullException(nameof(httpContext));
          if (routeData == null)
            throw new ArgumentNullException(nameof(routeData));
          this.HttpContext = httpContext;
          this.RouteData = routeData;
        }
        //请求上下文
        public virtual HttpContextBase HttpContext { get; set; }
        //路由数据
        public virtual RouteData RouteData { get; set; }
      }

    3、RouteData

       RouteBase的GetRouteData方法的返回类型,用于封装解析后路由数据,其用RouteValueDictionary保存路由变量数据,RouteValueDictionary是—个字典,其 Key和 Value分别表示路由变量的名称和值,定义如下:

      public class RouteValueDictionary : IDictionary<string, object>

      {

      }

      public class RouteData
      {
        private RouteValueDictionary _values = new RouteValueDictionary();
        private RouteValueDictionary _dataTokens = new RouteValueDictionary();
        private IRouteHandler _routeHandler;

        public RouteData()
        {
        }

        public RouteData(RouteBase route, IRouteHandler routeHandler)
        {
          this.Route = route;
          this.RouteHandler = routeHandler;
        }

        //是直接附加到Route上的自定义变量。
        public RouteValueDictionary DataTokens
        {
          get
          {
            return this._dataTokens;
          }
        }

        //解析它的 Route对象

        public RouteBase Route { get; set; }

        //提供最 终用 于处理请求的HttpHandIer对象(通过调用其GetHttpHandler方法获取)

        //可以在构造函数中传入,也可以属性赋值
        public IRouteHandler RouteHandler
        {
          get
          {
            return this._routeHandler;
          }
          set
          {
            this._routeHandler = value;
          }
        }

        //其中的路由变量是Route通过对请求URL的解析得到的
        public RouteValueDictionary Values
        {
          get
          {
            return this._values;
          }
        }

        //获取包含某些固定名称的变量值(如controller和action)对应的值
        public string GetRequiredString(string valueName)
        {
          object obj;
          if (this.Values.TryGetValue(valueName, out obj))
          {
            string str = obj as string;
            if (!string.IsNullOrEmpty(str))
              return str;
          }
          //不存在直接抛出异常
          throw new InvalidOperationException(string.Format((IFormatProvider)CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteData_RequiredValue"), new         object[1]
              {
                (object) valueName
              }));
        }
      }

      

      public interface IRouteHandler
      {
        IHttpHandler GetHttpHandler(RequestContext requestContext);
      }

    4、VirtualPathData

      RouteBase的GeVirtualPathData方法的返回类型

      public class VirtualPathData
      {
        private RouteValueDictionary _dataTokens = new RouteValueDictionary();
        private string _virtualPath;

        public VirtualPathData(RouteBase route, string virtualPath)
        {
          this.Route = route;
          this.VirtualPath = virtualPath;
        }

        //来源于附加到 Route的 自定义变量集合
        public RouteValueDictionary DataTokens
        {
          get
          {
            return this._dataTokens;
          }
        }
        //当时解析的路由对象
        public RouteBase Route { get; set; }

        //完整虚拟路径
        public string VirtualPath
        {
          get
          {
            return this._virtualPath ?? string.Empty;
          }
          set
          {
            this._virtualPath = value;
          }
        }
      }

    5、RouteTable Routes RouteCollection

      全局路由表,即RouteTable类的静态属性Routes 类型为RouteCollection,通过其中的MapPageRoute方法进行路由映射

      public class RouteTable
      {
        private static RouteCollection _instance = new RouteCollection();

        public static RouteCollection Routes
        {
          get
          {
            return RouteTable._instance;
          }
        }
      }

      RouteCollection 是Route的集合

      主要逻辑:

        RouteCollection的GetRouteData和GetVirtalPath方法会遍历集合中的每个Route对象,并传入给定的参数调用同名方法直到找到一个匹配的Route(返回值不为Null),并返回相应的RouteData和VirtaulPathData对象,如果集合中任何一个Route都不匹配,最终返回NULL

      读写锁的应用:

        RouteCollection这个集合对象不是线程安全的,使用ReaderWriterLockSlim进行线程读写同步,多个线程可以同时读,其他情况都不允许,一个线程写时,其他的线程不能读或写,一个线程在读时候,其他线程只能读,不能写,即多个线程只能读读,不能读写、写写;

        在对集合进行读取或更新时候,会调用GetReadLock和GetWriteLock方法获取读锁或写锁,返回的是内嵌私有类型:ReadLockDispsoabled和WriteLockDispsoabled,他们实现了接口IDispsoabled,是对ReaderWriterLockSlim的读写锁功能的封装。

      public class RouteCollection : Collection<RouteBase>
      {
        private Dictionary<string, RouteBase> _namedMap = new Dictionary<string, RouteBase>((IEqualityComparer<string>)StringComparer.OrdinalIgnoreCase);
        private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();

        public RouteCollection()
        {
        }

        public bool RouteExistingFiles { get; set; }


        public void Add(string name, RouteBase item)
        {
          //...
          this.Add(item);
          if (!string.IsNullOrEmpty(name))
            this._namedMap[name] = item;
          Route route = item as Route;
          if (route == null || route.RouteHandler == null)
            return;
        }

        //省略N个重载方法
        public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary       constraints, RouteValueDictionary dataTokens)
        {
          if (routeUrl == null)
            throw new ArgumentNullException(nameof(routeUrl));
          //新建路由对象,IRouteHandler直接new PageRouteHandler
          Route route = new Route(routeUrl, defaults, constraints, dataTokens, (IRouteHandler)new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));
          //添加进去
          this.Add(routeName, (RouteBase)route);
          return route;
        }

        protected override void ClearItems()
        {
          this._namedMap.Clear();
          base.ClearItems();
        }

        public IDisposable GetReadLock()
        {
          this._rwLock.EnterReadLock();
          return (IDisposable)new System.Web.Routing.RouteCollection.ReadLockDisposable(this._rwLock);
        }

        private RequestContext GetRequestContext(RequestContext requestContext)
        {
          if (requestContext != null)
            return requestContext;
          HttpContext current = HttpContext.Current;
          if (current == null)
            throw new InvalidOperationException(System.Web.SR.GetString("RouteCollection_RequiresContext"));
          return new RequestContext((HttpContextBase)new HttpContextWrapper(current), new RouteData());
        }

        private bool IsRouteToExistingFile(HttpContextBase httpContext)
        {
          string executionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
          if (!(executionFilePath != "~/") || this.VPP == null)
            return false;
          if (!this.VPP.FileExists(executionFilePath))
            return this.VPP.DirectoryExists(executionFilePath);
          return true;
        }

        public RouteData GetRouteData(HttpContextBase httpContext)
        {
          ...
          using (this.GetReadLock())
          {
            //遍历集合中所有RouteBase,并调用其GetRouteData方法,找到了就马上返回
            foreach (RouteBase routeBase in (Collection<RouteBase>)this)
            {
              RouteData routeData = routeBase.GetRouteData(httpContext);
              if (routeData != null)
              {
                if (!routeBase.RouteExistingFiles)
                {
                  if (!flag2)
                    flag1 = this.IsRouteToExistingFile(httpContext);
                  if (flag1)
                    return (RouteData)null;
                }
                return routeData;
              }
            }
          }
          return (RouteData)null;
        }


        public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
          requestContext = this.GetRequestContext(requestContext);
          using (this.GetReadLock())
          {
            //遍历集合中所有RouteBase,并调用其GetVirtualPath方法,找到了就马上返回
            foreach (RouteBase routeBase in (Collection<RouteBase>)this)
            {
              VirtualPathData virtualPath = routeBase.GetVirtualPath(requestContext, values);
              if (virtualPath != null)
              {
                virtualPath.VirtualPath = this.NormalizeVirtualPath(requestContext, virtualPath.VirtualPath);
                return virtualPath;
              }
            }
          }
          return (VirtualPathData)null;
        }

        public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values)
        {
          requestContext = this.GetRequestContext(requestContext);
          if (string.IsNullOrEmpty(name))
            return this.GetVirtualPath(requestContext, values);
          RouteBase routeBase;
          bool flag;
          using (this.GetReadLock())
            flag = this._namedMap.TryGetValue(name, out routeBase);
          if (flag)
          {
            VirtualPathData virtualPath = routeBase.GetVirtualPath(requestContext, values);
            if (virtualPath == null)
              return (VirtualPathData)null;
            virtualPath.VirtualPath = this.NormalizeVirtualPath(requestContext, virtualPath.VirtualPath);
            return virtualPath;
          }
          throw new ArgumentException(string.Format((IFormatProvider)CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteCollection_NameNotFound"), new         object[1]
            {
              (object) name
            }), nameof(name));
         }


        public IDisposable GetWriteLock()
        {
          this._rwLock.EnterWriteLock();
          return (IDisposable)new System.Web.Routing.RouteCollection.WriteLockDisposable(this._rwLock);
        }

        //忽略路由
        public void Ignore(string url)
        {
          this.Ignore(url, (object)null);
        }

        public void Ignore(string url, object constraints)
        {
          if (url == null)
            throw new ArgumentNullException(nameof(url));
          System.Web.Routing.RouteCollection.IgnoreRouteInternal ignoreRouteInternal = new System.Web.Routing.RouteCollection.IgnoreRouteInternal(url);
          RouteValueDictionary routeValueDictionary = new RouteValueDictionary(constraints);
          ignoreRouteInternal.Constraints = routeValueDictionary;
          this.Add((RouteBase)ignoreRouteInternal);
        }


        protected override void InsertItem(int index, RouteBase item)
        {
          if (item == null)
            throw new ArgumentNullException(nameof(item));
          if (this.Contains(item))
            throw new ArgumentException(string.Format((IFormatProvider)CultureInfo.CurrentCulture, System.Web.SR.GetString("RouteCollection_DuplicateEntry"), new           object[0]), nameof(item));
          base.InsertItem(index, item);
        }

        protected override void RemoveItem(int index)
        {
          this.RemoveRouteName(index);
          base.RemoveItem(index);
        }

        private void RemoveRouteName(int index)
        {
          RouteBase routeBase = this[index];
          foreach (KeyValuePair<string, RouteBase> named in this._namedMap)
          {
            if (named.Value == routeBase)
            {
              this._namedMap.Remove(named.Key);
              break;
            }
          }
        }


        protected override void SetItem(int index, RouteBase item)
        {
          if (item == null)
            throw new ArgumentNullException(nameof(item));
          if (this.Contains(item))
            throw new ArgumentException(string.Format((IFormatProvider)CultureInfo.CurrentCulture, System.Web.SR.GetString("RouteCollection_DuplicateEntry"), new           object[0]), nameof(item));
          this.RemoveRouteName(index);
          base.SetItem(index, item);
        }

        private class ReadLockDisposable : IDisposable
        {
          private ReaderWriterLockSlim _rwLock;

          public ReadLockDisposable(ReaderWriterLockSlim rwLock)
          {
            this._rwLock = rwLock;
          }

          void IDisposable.Dispose()
          {
            this._rwLock.ExitReadLock();
          }
        }

        private class WriteLockDisposable : IDisposable
        {
          private ReaderWriterLockSlim _rwLock;

          public WriteLockDisposable(ReaderWriterLockSlim rwLock)
          {
            this._rwLock = rwLock;
          }

          void IDisposable.Dispose()
          {
            this._rwLock.ExitWriteLock();
          }
        }

        private sealed class IgnoreRouteInternal : Route
        {
          public IgnoreRouteInternal(string url)
            : base(url, (IRouteHandler)new StopRoutingHandler())
          {
          }

          public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues)
          {
            return (VirtualPathData)null;
          }
        }
      } 

    6、IRouteConstraint

      除了用正则表达式对某个变量进行约束外,还可以用一个实现了IRouteConstraint接口的对象对整个请求进行约束

      public interface IRouteConstraint
      {
        bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
      }

    二、注册路由映射

    1、基本使用MapPageRoute方法

      注册路由映射核心是在全局路由表RouteTable.Routes里添加一个Route对象,通过调用路由表的MapPageRoute方法,该方法可以传递各种相关参数,如前一节的源码

      public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary       constraints, RouteValueDictionary dataTokens)

      可以指定路由名称routeName,模板routeUrl,对应处理的物理文件physicalFile,是否核对路由目标地址的URL授权,默认值defaults,约束constraints,附加到 Route的 自定义变量集合dataTokens

      如我们可以按下边传递参数:   

        var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};

        //约束,正则表达式
        var constaints = new RouteValueDictionary { { "areacode", @"0d{2,3}" }, { "days", @"[1-3]" } };

        //对变量默认值的说明
        var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };

        RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}","~/weather.aspx", false, defaults, constaints, dataTokens);

       由上边源码可知,会建立如下Route对象

        Route route = new Route(routeUrl, defaults, constraints, dataTokens, (IRouteHandler)new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));

       IRouteHandler是new PageRouteHandler(physicalFile, checkPhysicalUrlAccess)

    2、自定义约束IRouteConstraint

      接下来,我们用继承自IRouteConstraint约束类来限制请求允许的方法   

        public class HttpMethodConstraint : IRouteConstraint
        {
          public HttpMethodConstraint(params string[] allowedMethods);

          public ICollection<string> AllowedMethods { get; }

          protected virtual bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
        }

      使用方法:

         { "httpMethod", new HttpMethodConstraint("POST") } 

      当然,可以传多个

    3、直接路由物理文件(RouteExistingFiles)

      RouteCollection和RouteBase都有属性RouteExistingFiles ,默认值分别为 false和true,要成功匹配路由,要满足两个条件

      两者RouteExistingFiles 都为true,Route 的URL模板和请求URL匹配,否则不会获得RouteData数据。

      如我们按传统方式访问一个物理文件http://myhost:1111/my.aspx,路由配置一样

      RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}","~/my.aspx", false, defaults, constaints, dataTokens);

      my.aspx页面会打印成功匹配后的路由(RouteData)数据,运行结果显示,虽然成功匹配(days由默认值),但是处理页面my.aspx没有显示路由数据。所以要设置

      RouteTable.Routes.RouteExistingFiles = true;

    4、注册忽略路由(Ignore)

      IIS7.5以及在集成模式下,所有请求都要进入ASP.NET管道,那么我们要过滤掉一些css和js之类的请求,不对这些请求进行路由,通过全局路由表RouteCollection的Ignore方法,如下语句

      RouteTable.Routes.Ignore("css/{filename}.css/{*pathInfo}");

    5、路由注册第二种方式(Add方法)

      RouteTable.Routes.Add(new Route("{areacode}/{days}",defaults, constaints, dataTokens,new PageRouteHandler("~/my.aspx", false ));

    三、根据路由规则生成URL

      即使用RouteTable.Routes.GetVirtualPath,通过路由配置生成 URL ,好处是可以更改配置而无需担心在应用程序中创建的URL链接中断,下边是一个使用例子:

      private string GetVirtualPathForRecipe(string recipeName)

      {

        VirtualPathData pathData = 

          RouteTable.Routes.GetVirtualPath(
          null,
          "Recipe",
          new RouteValueDictionary { { "Name", recipeName } });

        return pathData.VirtualPath;

      }  

      Recipe是路由名称,路由模板为 (/recipe/{name}) ,new RouteValueDictionary { { "Name", recipeName }是传入的路由变量及其值

      var recipes =
        new RecipeRepository()
          .GetAllRecipeNames()
          .OrderBy(recipeName => recipeName)
          .Select(recipeName =>
            new
            {
              Name = recipeName,
              Url = GetVirtualPathForRecipe(recipeName)
            });

     四、如何获取最终处理请求的HttpHandler(UrlRoutingModule)

      HttpHandler是由以下接口获得的

      public interface IRouteHandler
      {
        IHttpHandler GetHttpHandler(RequestContext requestContext);
      }

      从前边源码,可以知道,路由注册时候就指定了PageRouteHandler作为IRouteHandler,其GetHttpHandler方法会返回处理physicalFile的IHttpHandler

      Route route = new Route(routeUrl, defaults, constraints, dataTokens, (IRouteHandler)new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));

      那什么时候让系统知道,处理当前请求是用那个Httphandler呢,利用拦截HttpModule来实现。UrlRoutingModule就是这个请求拦截器。定义如下:

      public class UrlRoutingModule : IHttpModule

      {

        public RouteCollection RouteCollection { get; set; }
        protected void Init(HttpApplication context)

        {
          context.PostResolveRequestCache += new EventHandler(this.OnPostResolveRequestCache);
        }
        void OnPostResolveRequestCache( object o, EventArgs e)

        {
          HttpContext context = ((HttpApplication)sender).Context;
          HttpContextBase contextWrapper = new HttpContextWrapper(context);
          //从当前请求获取路由解析后的数据RouteData
          var routeData = RouteCollection.GetRouteData (contextWrapper);
          //封装RouteData对象和当前HttpRequest对象为requestContext
          var requestContext = new RequestContext (context, routeData);
          //使用当前RouteData对象中的RouteHander属性获取路由处理程序IHttpHander接口
          IHttpHandler httpHandler = routeData.RouteHandler.GetHttpHandler (requestContext );
          context.Request.RequestContext = requestContext ;
          context.RemapHandler (httpHandler);

         }

      }

     
  • 相关阅读:
    Sublime Text3安装包管理
    Ubuntu下的Sun Java的安装与配置
    求最大面积--------O(n)复杂度
    求最大面积
    gas stations
    n皇后问题
    Triangle --- 至顶向下求最小值
    First Missing Positive
    常见的几种单例模式
    分布式锁学习笔记
  • 原文地址:https://www.cnblogs.com/shawnhu/p/7944856.html
Copyright © 2020-2023  润新知