17.8 ASP.NET路由
在不使用路由的ASP.NET应用程序中,对URL的传入请求通常映射到处理该请求的物理文件,如.aspx文件。例如,对http://www.comesns.com/application/Products.aspx?id=4的请求映射到名为Products.aspx的文件,该文件包含代码和标记用于呈现对浏览器的响应。网页使用查询字符串值id=4来确定要显示的内容类型。
在ASP.NET路由中,可以来自己定义URL模式,这些模式映射到请求处理程序文件但是不必在URL中包含这些文件的名称。另外,可以在URL模式中包含占位符,以便无须查询字符串,即可将变量数据传递到请求处理程序。
例如,在请求http://www.comesns.com/application/Products/show/beverages时,路由分析器可以将值Products、show和beverages传递给页处理程序。在此示例中,如果路由是使用URL模式server/application/{area}/{action}/{category}定义的,则页处理程序将收到一个字典集合,在该集合中,与键area关联的值为Products,键action的值为show,键category的值为beverages。而在不由URL路由管理的请求中,/Products/show/beverages片断将被解释为应用程序中一个文件的路径。
17.8.1 路由与URL模式
在ASP.NET中,“路由”是一个什么样的概念呢?
简单地讲,“路由”是映射到处理程序的URL模式。处理程序可以是物理文件,例如Web窗体应用程序中的.aspx文件,也可以是处理请求的类。如果要定义路由,可以通过指定URL模式、处理程序和路由名称(可选)来创建Route类的一个实例。
通过将Route对象添加到RouteTable类的静态Routes属性,向应用程序中添加路由。其中,Routes属性是一个存储应用程序的所有路由的RouteCollection对象。
那么,什么又是URL模式呢?
URL模式可以包含文本值和变量占位符(也称为“URL参数”)。其中,文本和占位符位于由斜杠(/)字符分隔的URL段中。
当生成请求时,URL分析为段和占位符,变量值提供给请求处理程序。此过程类似于分析查询字符串中的数据并将该数据传递给请求处理程序的方法。在两种情况下,变量信息都包括在URL中并以键值对的形式传递给处理程序。对于查询字符串,键和值都位于URL中。对于路由,键是在URL模式中定义的占位符名称,只有值位于URL中。
在URL模式中,可以通过用大括号({和})括住占位符的方式来定义占位符。可以在一个段中定义多个占位符,但必须用一个文本值分隔开。例如,{language}-{country}/{action}是有效的路由模式,但是{language}{country}/{action}却不是有效的模式,因为路由无法确定在哪里将language占位符的值与country占位符的值分隔开。
表17-5演示有效的路由模式和一些与模式匹配的URL请求的示例。
17.8.2 添加与使用路由
如果要向应用程序中添加路由,可以通过使用RouteCollection类的MapPageRoute方法来创建路由。其中,MapPageRoute方法提供用于定义Web窗体应用程序的路由的方法,其原型如下面的代码所示:
public Route MapPageRoute(
string routeName,//路由的名称
string routeUrl,//路由的URL模式
string physicalFile//路由的物理URL
)
通过该方法创建一个Route对象,并将其添加到RouteCollection对象中。当然,可以在传递到MapPageRoute方法的参数中指定Route对象的属性。
通常情况下,在Global.asax文件中Application_Start事件的处理程序调用的方法中添加路由。该方法可确保应用程序启动时路由可用,还可以使你能够在对应用程序进行单元测试时直接调用方法。如果想在对应用程序进行单元测试时直接调用一个注册路由的方法,则该方法必须是静态的,并且必须具有一个RouteCollection参数。
下面的示例演示从Global.asax文件中添加一个Route对象的代码,此代码添加了一个未命名的路由,该路由具有URL匹配模式,该模式包含文本值"SalesReportSummary"和名为year的占位符((UL参数)。它将路由映射到名为Sales.aspx的文件。如下面的代码所示:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapPageRoute("","SalesReportSummary/{year}",
"~/sales.aspx");
}
当然,也可以为路由命名,如下面再添加一个名为SalesRoute的路由。
routes.MapPageRoute("SalesRoute",
"SalesReport/{locale}/{year}",
"~/sales.aspx");
除了按照URL中的参数数量将URL请求匹配到路由定义中之外,还可以指定参数中的值来满足特定约束。如果一个URL包含路由的约束以外的值,则该路由不用于处理请求。添加约束以确保URL参数包含将在应用程序中起作用的值。
约束是通过使用正则表达式或使用实现IRouteConstraint接口的对象来定义的。将路由定义添加到Routes集合时,同时也通过创建一个包含验证测试的RouteValueDictionary对象添加了约束。字典中的关键字标识约束适用的参数。字典中的值可以是表示正则表达式的字符串,也可以是实现IRouteConstraint接口的对象。
提供字符串后,路由将视字符串为正则表达式,并通过调用Regex类的IsMatch方法检查参数值是否有效。总是将正则表达式视为不区分大小写。
提供IRouteConstraint对象后,ASP.NET路由将通过调用IRouteConstraint对象的Match方法检查参数值是否有效。Match方法返回一个布尔值,该值指示参数值是否有效。
如下面的示例演示如何使用MapPageRoute方法创建具有约束的路由。其中,代码将locale参数的默认值设置为"US",将year参数的默认值设置为今年。约束指定locale参数必须由两个字母字符组成,而year参数必须由四个数字组成。代码如下所示:
routes.MapPageRoute("ExpensesRoute",
"ExpenseReport/{locale}/{year}/{*extrainfo}",
"~/expenses.aspx",true,
new RouteValueDictionary{
{"locale","US"},
{"year",DateTime.Now.Year.ToString()}},
new RouteValueDictionary{
{"locale","[a-z]{2}"},
{"year",@"\d{4}"}});
创建好路由之后,就可以使用路由创建超链接。当向网页中添加超链接时,如果希望指定路由URL而不是物理文件,则可以有两个选择:
1)可以对路由URL进行硬编码。如下面的代码所示:
<asp:HyperLink ID="HyperLink1"runat="server"
NavigateUrl="~/salesreportsummary/2010">
Sales Report-All locales,2010
</asp:HyperLink>
<br/>
<asp:HyperLink ID="HyperLink2"runat="server"
NavigateUrl="~/salesreport/WA/2011">
Sales Report-WA,2011
</asp:HyperLink>
<br/>
<asp:HyperLink ID="HyperLink3"runat="server"
NavigateUrl="~/expensereport">
Expense Report-Default Locale and Year(US, current year)
</asp:HyperLink>
<br/>
2)可以指定路由参数名称和值,并让ASP.NET生成对应的URL。如果有必要,还可以指定路由名称,以便唯一标识路由。如果稍后更改路由URL规则,则必须更新所有硬编码的URL,但是如果让ASP.NET生成URL,则始终自动生成正确的URL(除非模式中的参数已更改)。如下面的代码所示:
<asp:HyperLink ID="HyperLink4"runat="server"
NavigateUrl="<%$RouteUrl:year=2011%>">
Sales Report-All locales,2011
</asp:HyperLink>
<br/>
<asp:HyperLink ID="HyperLink5"runat="server"
NavigateUrl="<%$RouteUrl:locale=CA, year=2009,
routename=salesroute%>">
Sales Report-CA,2009
</asp:HyperLink>
<br/>