Asp.NET中的路由配置,首先要提到的几个相关类型,Route
,RouteBase,RouteData,RouteTable,RouteCollection
这几个。下面我一次回忆一下,有需要的朋友也可以作为参考,如果有问题也可以在留言区指出来,高手路过也请提出一些宝贵的意见。

Asp.net mvc 中,来自客户端的请求总是针对某个 Controller 中的
Action 方法,因此,必须采用某种机制从请求的 URl 中解析出对应的
ControllerAction 的名称,这个过程便称为路由(Routing)

 

①Route
这个类继承了RouteBase这个抽象类,Route类型中几个重要的属性:RouteHandler,它是最重要的一个属性,作用是真正执行路由的功能。这里有个小技巧 当你看到DemoHandler
类似这样的以Handler结尾的时候 该属性是最后用来执行的。

路由(Routing) 机制并不是专属于 Asp.net mvc,而是建立在
Asp.net 上的,与其相关的核心类型都定义在 System.Web 程序集中

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

1)Constaints
约束,路由模板约束
,它的类型是RouteValueDictionary字典类型key表示的是路由模板中的变量名称,value表示匹配的正则表达式。

路由机制的优势

  与之前的 Web From
应用的每次请求的都是针对某一物理文件相比,路由机制具有如下的优势:

  • 灵活性。当请求的物理文件的名称或者路径发生改变时,无需修改现有的路由,因为路由将物理文件的路径与请求完全的分离
  • 可读性。采用路由机制后,可以很容易的从请求的 Url
    中看出本次请求的目的或或者要获取的信息
  • SEO优化。路由机制下的 Url 更符合搜索引擎的检索规则。

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

3)Defaults

核心类型

  • RouteBase
    路由机制中的所有的路由对象都为抽象类 RouteBase
    的子类,其定义如下:

    public abstract class RouteBase
    {
        private bool _routeExistingFiles = true;
    
        protected RouteBase()
        {
        }
    
        public abstract RouteData GetRouteData(HttpContextBase httpContext);
        public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    
        public bool RouteExistingFiles
        {
            get => 
                this._routeExistingFiles;
            set
            {
                this._routeExistingFiles = value;
            }
        }
    }
    

  从中可以看出,其有两个核心的方法 GetRouteData
GetVirtualPath,前者用于根据当前的 HttpContext
去获取与当前请求匹配的 RouteData,后者用于根据匹配的路由和传递的
Values 生成 Url,其中的参数 RequestContext 是对
HttpContextRouteData 的封装。

  • Route
      默认情况下,RouteBase 具有一个实现类
    Route,其定义如下(部分):

    public class Route : RouteBase
    {
        public Route(string url, IRouteHandler routeHandler)
    
        public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
    
        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
    
        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
    
        public override RouteData GetRouteData(HttpContextBase httpContext)
    
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    
        public RouteValueDictionary Constraints { get; set; }
    
        public RouteValueDictionary DataTokens { get; set; }
    
        public RouteValueDictionary Defaults { get; set; }
    
        public IRouteHandler RouteHandler { get; set; }
    
        public string Url {get;set;}
    }
    

      其中的 Url 表示注册的路由的模板,例如
    /{controller}/{action}/{id}, Defaults 表示路由的默认值,例如
    new {controller="home",action="index",id = UrlParameter.Optional},
    UrlParameter.Optional 表示该参数是可选的 , constraints 表示
    对当前定义的路由规则的一些约束,例如,constrains:new {id = @"d+"},限制
    参数id为数字 例如将RouteHandler 表示对匹配该路由的请求处理使用的
    IRouteHandler 类型的对象,其具有一个 返回 IHttpHandler
    GetHttpHandler(RequestContext context)的方法,IHttpHandler
    中定义了一个 void ProcessRequest(HttpContext context) 是整个
    Asp.net 应用进行后续处理的入口。

  • RouteData
      RouteData 中封存通过路由机制从请求的 Url
    中解析出来的路由数据,其定义如下(部分):
      

    public class RouteData
    {
        public RouteData()
    
        public RouteData(RouteBase route, IRouteHandler routeHandler)
    
        public string GetRequiredString(string valueName)
    
        public RouteValueDictionary DataTokens{get;}
    
        public RouteBase Route { get; set; }
    
        public IRouteHandler RouteHandler{get;set;}
    
        public RouteValueDictionary Values{get;}
    }
    

      上面的 Route 对象的 GetRouteData 返回的就是这样的一个
    RouteData 对象,其中的 Route
    就是与当前请求匹配的路由对象,RouteHandler 对应着 Route
    对象的同名属性,Values 中保存从请求 Url
    中解析出来的数据(键值对),如 controlleractionid
    等,DataTokens 亦对应着 Route 对象的同名属性 。
      GetRequiredString(string valueName) 方法根据指定的
    Key(valueName) 从 Values 中获取对应的值。如果指定的 key
    不存在,则会抛出 InvalidOperation 的异常。

  • RouteTable
      RouteTable 类的定义比较简单,除了构造函数外,只有一个
    RouteCollection 类型的静态属性
    Routes,全局的路由注册便是通过该属性进行操作的。
      RouteCollection 派生自 Collection,其定义如下(部分核心):

    public class RouteCollection : Collection<RouteBase>
    {
        private Dictionary<string, RouteBase> _namedMap;
        private ReaderWriterLockSlim _rwLock;
        private VirtualPathProvider _vpp;
    
        public RouteCollection()
    
        public RouteCollection(VirtualPathProvider virtualPathProvider)
    
        public RouteData GetRouteData(HttpContextBase httpContext)
    
        public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    
        public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values)
        public void Ignore(string url)
    
        public void Ignore(string url, object constraints)
    
        public Route MapPageRoute(string routeName, string routeUrl, string physicalFile) 
    
        public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens)
    
        public bool AppendTrailingSlash { get; set; }
    
        public RouteBase this[string name]
    
        public bool LowerCaseUrls{get;set;}
        public bool RouteExistingFiles { get; set; }
    

    }

  上面的代码可以看到其中定义了
RouteData GetRouteData(HttpContextBase context)
VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values),在调用这两个方法时内部是通过调用集合内每个
Route 的同名方法,返回匹配的项,如果不存在则返回 null
  Ignore(string url) 方法将无需通过路由的资源对应的 Url
添加到路由表中,其内部会调用 Add 方法,参数 constraint 的作用同
Route 对象的同名参数,即对当前的路由进行约束。
  Route MapPageRoute()
方法,将指定的路由信息注册到当前的路由表中,其内部会调用 Add
方法,因此,除了使用 MapPageRoute 方法进行路由注册外,还可以直接调用
Add 方法,两者唯一的区别就是前者会返回一个添加的 Route 对象
,后者则无返回值。 添加的 Route 对象的 IRouteHandler 均为
PageRouteHandler

  • AppendTrailingSliash 标识是否在生成的 Url 后添加 /
  • LowerCaseUrls 标识是否将生成的 Url 转换为小写
  • RouteExistingFiles 标识是否对物理文件进行路由

  在 Asp.net mvc 中对 RouteCollection
类进行了扩展,扩展方法定义在 System.Web.Mvc 程序集的
HttpRouteCollectionExtensions 类中,定义如下(部分核心方法):

    public static void IgnoreRoute(this RouteCollection routes, string url);
    public static void IgnoreRoute(this RouteCollection routes, string url, object constraints);
    public static Route MapRoute(this RouteCollection routes, string name, string url);
    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults);
    public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);
    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);

  上面的代码定义了两组方法,其中 IgnoreRoute 用于注册需要被忽略的
url 对应的路由模板,对应 RouteCollectionIgnore
方法,MapRoute 用于注册路由,对应 RouteCollection
MapPageRoute 方法。但与 MapPageRoute 不同的是原来的 constraints
defaultsRouteValueDictionary 变为了
object,这样我们可以通过匿名参数的方式来为其赋值,在方法的内部会通过反射的方式获取对象的属性列表,然后将其转换为
RouteValueDictionary,其 keyvalue
分别为属性的名称与属性值。MapPageRoute 方法中调用 Add 方法时指定的
IRouteHanndlerPageRouteHandlerMapRoute 方法内部调用
Add 方法时指定的 IRouteHandlerMvcRouteHandler

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


RouteBase这个抽象类中主要有两个重要的方法
GetRouteData和GetVirtualPath(获取完整的虚拟路径)

路由解析整体过程

图片 1

一、涉及的类及源码分析

③RouteData

路由扩展(定制)思路

  • 通过继承 RouteBase 抽象类自定义路由实现自定义路由逻辑
  • 通过实现 IRouteHandler 接口自主实现 RouteHandler
    的提供机制(RouteCollection.Add(name,IRouteHandler))
  • 通过实现 IHttpHandler
    接口自主实现请求的处理并返回(HttpContextBase.RemapHandler(IHttpHandler))

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

1)Values,表示最后得到的参数

   图片 2

2)RouteHandler

1、RouteBase

④RouteTable这个类可以用来在asp.net应用程序启动的时候进行Url重写,重写的方法有2个,MapPageRoute和Ignore,第一个是物理文件和路由Url之间的映射,另外一个是忽略物理文件和路由Url之间的映射。即屏蔽。

  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页面

protected void
Application_Start()
{

    //但是有时候我们希望以物理文件路径方式来访问对应的物理文件,可以设置该值为false,就可以访问到Index.aspx页面
    public bool RouteExistingFiles
    {
      get
      {
        return this._routeExistingFiles;
      }
      set
      {
        this._routeExistingFiles = value;
      }
    }
  }

    ///路由重写
    var defaults =
new RouteValueDictionary { { “name”, “” }, { “id”, “” } };

2、Route

    RouteTable.Routes.MapPageRoute(“”, “api/{name}/{id}”,
“~/default.aspx”, true, defaults);

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

    GlobalConfiguration.Configure(WebApiConfig.Register);
}

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

⑤RouteCollection是Route的集合

    //构造函数,前边省略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;
    }

发表评论

电子邮件地址不会被公开。 必填项已用*标注