zoukankan      html  css  js  c++  java
  • ASP.NET 路由系统

    asp.net 的url路由系统,最初是为了实现Url与物理文件路径的分离而建立的,后来的asp.net mvc也是对asp.net路由系统的扩展,将url与物理文件映射转为url与目标controller/action的映射。

    1.请求的URL与物理文件的分离

    简单通过一个Demo来演示:
    场景:一个页面展示员工列表,点击员工姓名显示员工详细信息

    通过图片可以看出员工列表地址为:http://..../employees,当点击员工姓名链接后该员工的详细信息呈现出来了,在仔细观察其url格式为http://.../employees/{姓名}/{ID},对于熟悉asp.net mvc的童鞋肯定会知道这是其路由系统来实现的,但是URL路由系统是建立在ASP.NET上的,而非asp.net mvc 专有的,只是对它的一个扩展。
    代码下载:
    介绍一下路由规则:

    using System;
    //核心的程序集
    using System.Web.Routing;
    
    namespace WebApp
    {
        public class Global : System.Web.HttpApplication
        {
            protected void Application_Start(object sender, EventArgs e)
            {
                //定义一个路由模版,并注册到全局路由表
    
                //URL模版的默认值
                var defaults = new RouteValueDictionary { { "name", "*" }, { "id", "*" } };
                //核心方法:将某个物理文件映射到一个URL模版
                //本质是创建一个指定的URL模版(如下规则),然后添加到整个WebApp的全局路由表Routes
                RouteTable.Routes.MapPageRoute("", "employees/{name}/{id}", "~/Default.aspx", true, defaults); 
    
            }
        }
    }

    RouteValueDictionary在路由模版中为路由变量("{name}"和"{id}")指定默认值为*,意思是说针对"http://..../employees"的请求,路由系统会把它格式成"http://..../employees/*/*"
    然后在Default.aspx页面后台代码page_load事件中来接受employee的id,这里如果请求的URL与注册的路由模版的模式匹配,就会返回一个RouteData对象,其中Values是个字典对象,它包含请求的URL的变量信息(即对应的name和id的值),如下图所示

    如果是*或者null则返回所有员工与Gridview绑定,隐藏DetailsView,否则返回某员工的详细信息与DetailsView绑定,隐藏Gridview
    代码:

    Defualt.aspx.cs
    using System;
    using System.Web.UI;
    
    namespace WebApp
    {
        public partial class Default : Page
        {
            private EmployeeRepository repository;
    
            public EmployeeRepository Repository
            {
                get
                {
                    return null == repository ? repository = new EmployeeRepository() : repository;
                }
            }
    
            protected void Page_Load(object sender, EventArgs e)
            {
                if (this.IsPostBack)
                {
                    return;
                }
                string employeeId = this.RouteData.Values["id"] as string;
                if (employeeId == "*" || string.IsNullOrEmpty(employeeId))
                {
                    this.GridViewEmployees.DataSource = this.Repository.GetEmployees();
                    this.GridViewEmployees.DataBind();
                    this.DetailsViewEmployee.Visible = false;
                }
                else
                {
                    var employees = this.Repository.GetEmployees(employeeId);
                    this.DetailsViewEmployee.DataSource = employees;
                    this.DetailsViewEmployee.DataBind();
                    this.GridViewEmployees.Visible = false;
                }
            }
        }
    }

     2.Route与RouteTable
    上面的例子中谈到了全局路由表、路由对象等,下面具体的详细介绍
    RouteBase
    类型:抽象类
    成员:

    1. 抽象方法GetRouteData,参数类型:HttpContextBase返回一个封装了路由信息的RouteData对象,具体的参看上面截图或msdn
      RouteData
      // 摘要:
          //     封装有关路由的信息。
          [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
          public class RouteData
          {
              // 摘要:
              //     初始化 System.Web.Routing.RouteData 类的新实例。
              public RouteData();
              //
              // 摘要:
              //     使用指定路由和路由处理程序初始化 System.Web.Routing.RouteData 类的新实例。
              //
              // 参数:
              //   route:
              //     一个定义路由的对象。
              //
              //   routeHandler:
              //     一个处理请求的对象。
              public RouteData(RouteBase route, IRouteHandler routeHandler);
      
              // 摘要:
              //     获取在 ASP.NET 路由确定路由是否匹配请求时,传递到路由处理程序但未使用的自定义值的集合。
              //
              // 返回结果:
              //     一个包含自定义值的对象。
              public RouteValueDictionary DataTokens { get; }
              //
              // 摘要:
              //     获取或设置表示路由的对象。
              //
              // 返回结果:
              //     一个表示路由定义的对象。
              public RouteBase Route { get; set; }
              //
              // 摘要:
              //     获取或设置处理所请求路由的对象。
              //
              // 返回结果:
              //     一个处理路由请求的对象。
              public IRouteHandler RouteHandler { get; set; }
              //
              // 摘要:
              //     获取路由的 URL 参数值和默认值的集合。
              //
              // 返回结果:
              //     一个对象,其中包含根据 URL 和默认值分析得出的值。
              public RouteValueDictionary Values { get; }
      
              // 摘要:
              //     使用指定标识符检索值。
              //
              // 参数:
              //   valueName:
              //     要检索的值的键。
              //
              // 返回结果:
              //     其键与 valueName 匹配的 System.Web.Routing.RouteData.Values 属性中的元素。
              //
              // 异常:
              //   System.InvalidOperationException:
              //     valueName 的值不存在。
              public string GetRequiredString(string valueName);
          }

      Values和DataTokens两个属性都是RouteValueDictionary类型的对象,RouteValueDictionary是一个用于保存路由变量的字典,其中Key和Value分别表示变量的名称和值。
      RouteData的Values属性的变量是路由对象通过对请求的URL解析得到的。
      DataTokens属性则是直接附加到路由对象上的自定义变量。
      GetRequiredString方法用于获取指定名称的变量值,例如获取MVC中controller或action的值
      RouteHandler:实现url和物理文件映射的核心对象,用于处理请求的HttpHandler对象,当成功请求到某个.aspx页面后,通过匹配路由对象的GetRouteData方法得到的RouteData对象被直接附加到目标页面对应的Page对象上

      public class Page:TempateControl,IHttpHandler
      {
             //其他成员public RouteData RouteData { get; }    
      }
    2. 抽象方法GetVirtualPath,参数类型:RequestContext 返回一个封装了Url的VirtualPathData对象
      当请求的URL与URL模版匹配,则将URL中的变量替换URL模版中的变量占位符生成一个虚拟路径,虚拟路径和路由对象被封装成一个VirtualPathData对象返回
      public class VirtualPathData
          {
              public VirtualPathData(RouteBase route, string virtualPath);
              //获取路由定义的自定义值集合
              public RouteValueDictionary DataTokens { get; }
              //获取或设置用于创建 URL 的路由
              public RouteBase Route { get; set; }
              //获取或设置依据路由定义创建的 URL。
              public string VirtualPath { get; set; }
          }

      RequestContext是URL路由系统和ASP.NET MVC路由体系中使用最频繁的类型,用于表示当前请求上下文,实际上是对HTTP上下文和RouteData的封装

      public class RequestContext
          {
              public RequestContext();
              public RequestContext(HttpContextBase httpContext, RouteData routeData);
              //获取有关 HTTP 请求的信息
              public virtual HttpContextBase HttpContext { get; set; }
              //获取有关所请求路由的信息
              public virtual RouteData RouteData { get; set; }
          }

    Route
     是抽象类RouteBase唯一的直接子类

    Route
    public class Route : RouteBase
        {
            //==构造函数==
            //使用指定的 URL 模式和处理程序类初始化 Route 类的新实例
            public Route(string url,IRouteHandler routeHandler);
            //使用指定的 URL 模式、默认参数值和处理程序类初始化 Route 类的新实例。
            public Route(string url,RouteValueDictionary defaults,IRouteHandler routeHandler);
            //使用指定的 URL 模式、默认参数值、约束和处理程序类初始化 Route 类的新实例
            public Route(string url,RouteValueDictionary defaults,RouteValueDictionary constraints,IRouteHandler routeHandler);
            //使用指定的 URL 模式、默认参数值、约束、自定义值和处理程序类初始化 Route 类的新实例。
            public Route(string url,RouteValueDictionary defaults,RouteValueDictionary constraints,RouteValueDictionary dataTokens,IRouteHandler routeHandler);
            
            //==常用方法==
            //返回有关所请求路由的信息
            public override RouteData GetRouteData(HttpContextBase httpContext);
            //返回与路由关联的 URL 的相关信息
            public override VirtualPathData GetVirtualPath(RequestContext requestContext,RouteValueDictionary values);
    
            //==属性==
            //获取或设置要在 URL 不包含所有参数时使用的值。
            public RouteValueDictionary Defaults { get; set; }
            //获取或设置为 URL 参数指定有效值的表达式的词典
            public RouteValueDictionary Constraints { get; set; }
            //获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。
            public RouteValueDictionary DataTokens { get; set; }
            //获取或设置一个值,该值指示 ASP.NET 路由操作是否应处理与现有文件匹配的 URL。
            public bool RouteExistingFiles { get; set; }
            //获取或设置处理路由请求的对象。
            public IRouteHandler RouteHandler { get; set; }
            //获取或设置路由的 URL 模式。
            public string Url { get; set; }
        }

    演示如何创建 Route 对象并将其添加到 Routes 属性中

    Demo
    void Application_Start(object sender, EventArgs e) 
    {
        RegisterRoutes(RouteTable.Routes);
    }
    
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.Add(new Route
        (
             "Category/{action}/{categoryName}"
             , new CategoryRouteHandler()
        ));
    }

     RouteTable
    一个Web应用通过RouteTable的静态只读属性Routes维护一个全局的路由表

    public class RouteTable
        {
            public static RouteCollection Routes { get; }
        }
    RouteCollection
     public class RouteCollection : Collection<RouteBase>
        {
            //其他成员
            //返回有关集合中与指定值匹配的路由的信息
            public RouteData GetRouteData(HttpContextBase httpContext);
            //如果具有指定的上下文和参数值,则返回与路由关联的 URL 路径的相关信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
            //如果具有指定的上下文、路由名称和参数值,则返回与命名路由关联的 URL 路径的相关信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
            //定义不需检查是否匹配路由的 URL 模式
            public void Ignore(string url);
    
            /***eg:
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    "Default",                                              // Route name
                    "{controller}/{action}/{id}",                           // URL with parameters
                    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
                );
            }
            protected void Application_Start()
            {
                RegisterRoutes(RouteTable.Routes);
            }
            */
            //定义一个 URL 模式,此模式在请求 URL 满足指定约束的情况下不需要检查 URL 是否与路由匹配
            public void Ignore(string url,Object constraints);
            //eg:routes.Ignore("{*allaspx}", new {allaspx=@".*\.aspx(/.*)?"});
            //提供用于定义 Web 窗体应用程序的路由的方法
            public Route MapPageRoute(
                        string routeName,
                        string routeUrl,
                        string physicalFile,
                        bool checkPhysicalUrlAccess,
                        RouteValueDictionary defaults,
                        RouteValueDictionary constraints,
                        RouteValueDictionary dataTokens
                    );
            /*
             * routes.MapPageRoute("ExpenseDetailRoute",
                            "ExpenseReportDetail/{locale}/{year}/{*queryvalues}", "~/expenses.aspx",
                            false,
                            new RouteValueDictionary 
                                { { "locale", "US" }, { "year", DateTime.Now.Year.ToString() } },
                            new RouteValueDictionary 
                                { { "locale", "[a-z]{2}" }, { "year", @"\d{4}" } },
                            new RouteValueDictionary 
                                { { "account", "1234" }, { "subaccount", "5678" } });
    
             */
        }

    对现有文件路由

    注册路由:

    protected void Application_Start(object sender, EventArgs e)
            {
                var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
                var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
                RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}","~/weather.aspx", false, defaults, null, dataTokens);
            }

    weather页面用来展示当前RouteData的各项属性,当直接请求http://.../weather.aspx时,页面的RouteData各项属性都为空,所以ASP.NET没有对请求地址实施路由
    解决方案:在注册路有前加上一句:RouteTable.Routes.RouteExistingFiles=true;这句话是说需要对存在的物理文件实施路由,也就是说如果请求的URL与某个物理文件的路径地址一致的情况下是否还需要对其实施路由,该属性默认值为false,

    注册路由忽略地址

    但问题出现了,我们在页面weather.aspx引用了样式Style.css,采用RouteExistingFiles=true,意味着Style.css访问也会被路由,然后导向weather.aspx这个页面,这样一来无法正常在页面引用js和css文件了。
    解决方案:
    在注册路由之前加上一句:RouteTable.Routes.Ignore("{filename}.css/{*pathInfo}");,但是这句话必须在路由注册之前执行。

    直接添加路由对象

    我们调用RouteCollection对象的MapPageRoute方法进行路由注册的本质是在路由字典中添加Route,所以我们可以自己手工Add一个Route对象,

    //路由注册方式1
              routes.MapPageRoute("ExpenseDetailRoute",
                            "ExpenseReportDetail/{locale}/{year}/{*queryvalues}", "~/expenses.aspx",
                            false,
                            new RouteValueDictionary 
                                { { "locale", "US" }, { "year", DateTime.Now.Year.ToString() } },
                            new RouteValueDictionary 
                                { { "locale", "[a-z]{2}" }, { "year", @"\d{4}" } },
                            new RouteValueDictionary 
                                { { "account", "1234" }, { "subaccount", "5678" } });
    
             //路由注册方式2
            Route route=new Route("ExpenseReportDetail/{locale}/{year}/{*queryvalues}",
                                    new RouteValueDictionary 
                                { { "locale", "US" }, { "year", DateTime.Now.Year.ToString() } },
                                    new RouteValueDictionary 
                                { { "locale", "[a-z]{2}" }, { "year", @"\d{4}" } },
                                    new RouteValueDictionary 
                                { { "account", "1234" }, { "subaccount", "5678" } },
                                new PageRouteHandler("~/expenses.aspx",false));
            RouteTable.Routes.Add("default",route);

    2.根据路由规则生成URL

    ASP.NET的路由系统主要有两个方面的应用

    1. 通过注册URL模版与物理文件的匹配实现请求地址和物理地址的分离
    2. 通过注册的路由规则生成一个响应的URL,通过RouteCollection的GetVirtualPath方法来实现
        public class RouteCollection : Collection<RouteBase>
        {
            //其他成员..
    
            //如果具有指定的上下文和参数值,则返回与路由关联的 URL 路径的相关信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
            //如果具有指定的上下文、路由名称和参数值,则返回与命名路由关联的 URL 路径的相关信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
        }

    GetVirtualPath方法
    如果requestContext参数为null,这种情况会基于当前HTTP上下文(对应HttpContext的静态属性Current)创建一个RequestContext对象作为GetVirtualPath的参数。如果RouteData为空,则抛出异常
    路由对象针对GetVirtualPath方法而进行的路由匹配只要求URL模版中定义的变量的值都能被提供,这些变量值具有3个来源:

    1. 路由对象定义的默认变量值
    2. 指定RequestContext的RouteData提供的变量值(Values属性)
    3. 手工提供的变量值(通过values参数指定的RouteValueDictionary对象)
        public class RouteCollection : Collection<RouteBase>
        {
            //其他成员..
    
            //如果具有指定的上下文和参数值,则返回与路由关联的 URL 路径的相关信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
            //如果具有指定的上下文、路由名称和参数值,则返回与命名路由关联的 URL 路径的相关信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);  
        }
            protected void Page_Load(object sender,EventArgs e)
            {
                //1.指定RequestContext的RouteData提供的Values属性
                RouteData routeData =new RouteData();
                routeData.Values.Add("areaCode","010");
                routeData.Values.Add("days","2");
                //封装到RequestContext中
                RequestContext requestContext=new RequestContext();
                //包装当前HttpContext
                requestContext.HttpContext =new HttpContextWrapper(HttpContext.Current);
                requestContext.RouteData=routeData;
                
                //2.手工指定参数values
                RouteValueDictionary values=new RouteValueDictionary();
                values.Add("areaCode","028");
                values.Add("days","3");
    
                Reponse.Write(RouteTable.Routes.GetVirtualPath(null,null).VirtualPath);//输出注册路有时指定的默认路由值
    
                Reponse.Write(RouteTable.Routes.GetVirtualPath(requestContext,null).VirtualPath);//输出:/010/2
    
                Reponse.Write(RouteTable.Routes.GetVirtualPath(requestContext,values).VirtualPath);//输出:/028/3
            }
  • 相关阅读:
    让你少奋斗10年的工作经验
    POJ Exponentiation解题
    数据结构树和二叉树
    语句摘录
    ACM解题报告格式
    编程规范
    数据结构图
    Java学习之二Java反射机制
    使用Python正则表达式提取搜索结果中的站点
    toj 1702 A Knight's Journey
  • 原文地址:https://www.cnblogs.com/hailiang2013/p/2875319.html
Copyright © 2011-2022 走看看