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

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

    " }, { "days", 2 }};

    //约束,正则表达式
    var constaints = new RouteValueDictionary { { "areacode", @"0\d{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);

     }

  }

 

ASP.NET Web API 框架研究 ASP.NET 路由的更多相关文章

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

    ASP.NET Web API 核心框架是一个独立的.抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到 ...

  2. ASP.NET Web API 框架研究 Action方法介绍

    在根据请求解析出匹配的Controller类型并创建实例后,要在该Controller类型中的众多Action方法中选择与请求匹配的那一个,并执行,然后返回响应. Action方法,其元数据,主要包括 ...

  3. ASP.NET Web API 框架研究 Controller实例的销毁

    我们知道项目中创建的Controller,如ProductController都继承自ApiController抽象类,其又实现了接口IDisposable,所以,框架中自动调用Dispose方法来释 ...

  4. ASP.NET Web API 框架研究 服务容器 ServicesContainer

    ServicesContainer是一个服务的容器,可以理解为—个轻量级的IoC容器,其维护着一个服务接口类型与服务实例之间的映射关系,可以根据服务接口类型获取对应的服务实例.构成ASP.NET We ...

  5. ASP.NET Web API 框架研究 核心的消息处理管道

    ASP.NET Web API 的核心框架是一个由一组HttpMessageHandler有序组成的双工消息处理管道:寄宿监听到请求接受后,把消息传入该管道经过所有HttpMessageHandler ...

  6. ASP.NET Web API 框架研究 IoC容器 DependencyResolver

    一.概念 1.IoC(Inversion of Control),控制反转 即将依赖对象的创建和维护交给一个外部容器来负责,而不是应用本身.如,在类型A中需要使用类型B的实例,而B的实例的创建不是由A ...

  7. ASP.NET Web API 框架研究 Self Host模式下的消息处理管道

    Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...

  8. ASP.NET Web API 框架研究 Web Host模式下的消息处理管道

    寄宿的作用是开启一个进程为Web API提供一个运行环境以解决持续监听.请求监听和响应回复,即将接收到的请求转换成HttpRequestMessage对象传入管道,并将管道生成并经过处理后的HttpR ...

  9. ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道

    Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...

随机推荐

  1. 异常检测(Anomaly Detection)

    十五.异常检测(Anomaly Detection) 15.1 问题的动机 参考文档: 15 - 1 - Problem Motivation (8 min).mkv 在接下来的一系列视频中,我将向大 ...

  2. Django web project

    在virtualenv下 (myvenv) ~/djangogirls$ django-admin startproject mysite . 生成web 工程目录 djangogirls ├───m ...

  3. C#动态显示时间

    private void timer1_Tick(object sender, EventArgs e) { DateTime dt = DateTime.Now; label1.Text = dt. ...

  4. 外部javascript形式

    ***.js: /** * 收起或者展开筛选框 */ function filterType(){ $("#filter_box_id").toggle(500); var sha ...

  5. JS代码判断浏览器类型以及版本

    browserVersion:function(){ var explorer = window.navigator.userAgent; if (explorer.indexOf("MSI ...

  6. mysql 批量杀进程

    select concat('KILL ',id,';') from information_schema.processlist where user='root';

  7. oracle unix时间戳与date转换

    linux 时间戳 转date:   创建自定义函数: create or replace function unix_to_oracle(in_number number) return date ...

  8. 01.Java 开发简单的计算器

    难度为一般,适合具有 Java 基础和 Swing 组件编程知识的用户学习一. 实验介绍1.1 实验内容本次实验利用Java开发一个可以进行简单的四则运算的图形化计算器,会使用到 Java Swing ...

  9. 2018.11.04 洛谷P1081 开车旅行(倍增)

    传送门 思路简单码量超凡? 感觉看完题大家应该都知道是倍增sbsbsb题了吧. 首先预处理出从每个点出发如果是AAA走到哪个点,如果是BBB走到哪个点. 然后利用刚刚预处理出的信息再预处理从每个点出发 ...

  10. 2018.10.31 bzoj4737: 组合数问题(lucas定理+容斥原理+数位dp)

    传送门 这是一道让我重新认识lucaslucaslucas的题. 考虑到lucaslucaslucas定理: (nm)≡(n%pm%p)∗(npmp)\binom n m \equiv \binom ...