ASP.NET路由
ASP.NET 路由使您可以使用不必映射到网站中特定文件的 URL。 由于该 URL 不必映射到文件,因此可以使用对用户操作进行描述因而更易于被用户理解的 URL。
ASP.NET MVC 框架和 ASP.NET 动态数据对路由进行扩展,以提供仅在 MVC 应用程序和动态数据应用程序中使用的功能。 有关 ASP.NET MVC 的更多信息,请参见 ASP.NET MVC。 有关动态数据的更多信息,请参见ASP.NET 动态数据内容映射。
在不使用路由的 ASP.NET 应用程序中,对 URL 的传入请求通常映射到处理该请求的物理文件,如 .aspx 文件。例如,对 http://server/application/Products.aspx?id=4 的请求映射到名为 Products.aspx 的文件,该文件包含代码和标记用于呈现对浏览器的响应。 网页使用查询字符串值 id=4 来确定要显示的内容类型。
在 ASP.NET 路由中,可以定义 URL 模式,这些模式映射到请求处理程序文件但是不必在 URL 中包含这些文件的名称。 另外,可以在 URL 模式中包含占位符,以便无需查询字符串,即可将变量数据传递到请求处理程序。
例如,在请求 http://server/application/Products/show/beverages 时,路由分析器可以将值Products、show 和 beverages 传递给页处理程序。 在此示例中,如果路由是使用 URL 模式server/application/{area}/{action}/{category} 定义的,则页处理程序将收到一个字典集合,在该集合中,与键 area 关联的值为 Products,键 action 的值为 show,键 category 的值为 beverages。 在不由 URL 路由管理的请求中,/Products/show/beverages 片断将被解释为应用程序中一个文件的路径。
本主题包含以下各节:
“路由”是映射到处理程序的 URL 模式。 处理程序可以是物理文件,例如 Web 窗体应用程序中的 .aspx 文件。 处理程序还可以是处理请求的类,例如 MVC 应用程序中的控制器。 若要定义路由,可以通过指定 URL 模式、处理程序和(可选)路由名称,创建 Route 类的一个实例。
通过将 Route 对象添加到 RouteTable 类的静态 Routes 属性,向应用程序中添加路由。 Routes 属性是一个存储应用程序的所有路由的 RouteCollection 对象。
通常不必编写代码来在 MVC 应用程序中添加路由。 MVC 的 Visual Studio 项目模板包含预先配置的 URL 路由。 它们是在 MvcApplication 类中定义的,该类是在 Global.asax 文件中定义的。
URL 模式可以包含文本值和变量占位符(也称为“URL 参数”)。 文本和占位符位于由斜杠 (/) 字符分隔的 URL 段中。
当生成请求时,URL 分析为段和占位符,变量值提供给请求处理程序。 此过程类似于分析查询字符串中的数据并将该数据传递给请求处理程序的方法。 在两种情况下,变量信息都包括在 URL 中并以键值对的形式传递给处理程序。 对于查询字符串,键和值都位于 URL 中。 对于路由,键是在 URL 模式中定义的占位符名称,只有值位于 URL 中。
在 URL 模式中,可以通过用大括号({ 和 })括住占位符来定义占位符。 可以在一个段中定义多个占位符,但必须用一个文本值分隔开。 例如,{language}-{country}/{action} 是有效的路由模式。 但是,由于占位符之间没有文本值或分隔符,所以 {language}{country}/{action} 不是有效的模式。 因此,路由无法确定在哪里将 language 占位符的值与 country 占位符的值分隔开。
下表演示有效的路由模式和一些与模式匹配的 URL 请求的示例。
路由定义 |
匹配 URL 示例 |
---|---|
{controller}/{action}/{id} |
/Products/show/beverages |
{table}/Details.aspx |
/Products/Details.aspx |
blog/{action}/{entry} |
/blog/show/123 |
{reporttype}/{year}/{month}/{day} |
/sales/2008/1/5 |
{locale}/{action} |
/US/show |
{language}-{country}/{action} |
/en-US/show |
MVC 应用程序中的典型 URL 模式
MVC 应用程序中用于路由的 URL 模式通常包括 {controller} 和 {action} 占位符。
当收到请求时,会将其路由到 UrlRoutingModule 对象,然后路由到 MvcHandler HTTP 处理程序。MvcHandler HTTP 处理程序通过向 URL 中的控制器值添加后缀“Controller”以确定将处理请求的控制器的类型名称,来确定要调用的控制器。 URL 中的操作值确定要调用的操作方法。
例如,包含 URL 路径 /Products 的 URL 映射到名为 ProductsController 的控制器。 action 参数中的值是调用的操作方法的名称。 包含 URL 路径 /Products/show 的 URL 将导致调用 ProductsController类的 Show 方法。
下表显示了默认 URL 模式,并给出由默认路由处理的 URL 请求的示例。
默认 URL 模式 |
匹配 URL 示例 |
---|---|
{controller}/{action}/{id} |
http://server/application/Products/show/beverages |
{resource}.axd/{*pathInfo} |
http://server/application/WebResource.axd?d=... |
包括了具有模式 {resource}.axd/{*pathInfo} 的路由,以避免对 Web 资源文件(例如 WebResource.axd 或 ScriptResource.axd)的请求传递给控制器。
对于 IIS 7.0,不需要文件扩展名。 对于 IIS 6.0,必须向 URL 模式添加 .mvc 文件扩展名,如下面的示例所示:
{controller}.mvc/{action}/{id}
在 Web 窗体应用程序中,通过使用 RouteCollection 类的 MapPageRoute(String, String, String) 方法来创建路由。 MapPageRoute 方法创建 Route 对象,并将其添加到 RouteCollection 对象。 可以在传递到MapPageRoute 方法的参数中指定 Route 对象的属性。
通常情况下,您在 Global.asax 文件中 Application_Start 事件的处理程序调用的方法中添加路由。 该方法可确保应用程序启动时路由可用。 还使您能够在对应用程序进行单元测试时直接调用方法。 如果您想在对应用程序进行单元测试时直接调用一个注册路由的方法,则该方法必须是静态的(Visual Basic 中的Shared),并且必须具有一个 RouteCollection 参数。
下面的示例演示从 Global.asax 文件中添加一个 Route 对象的代码,该对象定义了名为 action 和categoryName 的两个 URL 参数。 具有指定模式的 URL 定向到名为 Categories.aspx 的物理页。
如果通过创建派生自 ControllerBase 类的类并赋予它们以“Controller”结尾的名称来采纳实现控制器的 MVC 惯例,则不需要在 MVC 应用程序中手动添加路由。 预先配置的路由将调用您在控制器类中实现的操作方法。
如果想要在 MVC 应用程序中添加自定义路由,请使用 MapRoute(RouteCollection, String, String) 方法,不要使用 MapPageRoute(String, String, String) 方法。
下面的示例演示在 Global.asax 文件(该文件是在 MVC 应用程序的 Visual Studio 项目模板中定义的)中创建默认 MVC 路由的代码。
public class MvcApplication : System.Web.HttpApplication { 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 没有包括该参数的值,则会使用默认值。 通过将字典对象分配给 Route 类的 Defaults 属性,可以设置路由的默认值。 下面的示例演示如何使用MapPageRoute(String, String, String, Boolean, RouteValueDictionary) 方法添加具有默认值的路由。
void Application_Start(object sender, EventArgs e) { RegisterRoutes(RouteTable.Routes); } public static void RegisterRoutes(RouteCollection routes) { routes.MapPageRoute("", "Category/{action}/{categoryName}", "~/categoriespage.aspx", true, new RouteValueDictionary {{"categoryName", "food"}, {"action", "show"}}); }
ASP.NET 路由处理 URL 请求时,在示例中演示的路由定义(使用针对 categoryName 的 food 的默认值和针对 action 的 show 的默认值)得到下表列出的结果。
URL |
参数值 |
---|---|
/Category |
action = "show"(默认值) categoryName = "food"(默认值) |
/Category/add |
action = "add" categoryName = "food"(默认值) |
/Category/add/beverages |
action = "add" categoryName= "beverages" |
对于 MVC 应用程序,通过 RouteCollectionExtensions.MapRoute 方法的重载(例如MapRoute(RouteCollection, String, String, Object, Object))可以指定默认值。
有时您需要处理包含可变数量的 URL 段的 URL 请求。 定义路由时,可以指定 URL 是否具有比模式中更多的段,是否将额外的段视为最后一个段的一部分。 若要以此方式处理额外的段,可以用星号 (*) 标记最后一个参数。 该参数称为“可用于放置各种信息的”参数。 具有全部捕捉参数的路由也将与那些不包含最后一个参数的任意值的 URL 相匹配。 下面的示例演示一个与未知数量的段匹配的路由模式。
query/{queryname}/{*queryvalues}
ASP.NET 路由处理 URL 请求时,在示例中演示的路由定义得到下表列出的结果。
URL |
参数值 |
---|---|
/query/select/bikes/onsale |
queryname = "select" queryvalues = "bikes/onsale" |
/query/select/bikes |
queryname = "select" queryvalues = "bikes" |
/query/select |
queryname = "select" queryvalues = Empty string |
除了按照 URL 中的参数数量将 URL 请求匹配到路由定义中,还可以指定参数中的值满足特定约束。 如果一个 URL 包含路由的约束以外的值,则该路由不用于处理请求。 添加约束以确保 URL 参数包含将在应用程序中起作用的值。
约束是通过使用正则表达式或使用实现 IRouteConstraint 接口的对象来定义的。 将路由定义添加到 Routes集合时,同时也通过创建一个包含验证测试的 RouteValueDictionary 对象添加了约束。 字典中的关键字标识约束适用的参数。 字典中的值可以是表示正则表达式的字符串,也可以是实现 IRouteConstraint 接口的对象。
提供字符串后,路由将视字符串为正则表达式,并通过调用 Regex 类的 IsMatch 方法检查参数值是否有效。总是将正则表达式视为不区分大小写。 有关更多信息,请参见 .NET Framework 正则表达式。
提供 IRouteConstraint 对象后,ASP.NET 路由将通过调用 IRouteConstraint 对象的 Match 方法检查参数值是否有效。 Match 方法返回一个布尔值,该值指示参数值是否有效。
下面的示例演示如何使用 MapPageRoute 方法创建具有约束的路由,该约束限制可在 locale 和 year 参数中包括的值。 (在 MVC 应用程序中,使用 MapRoute 方法。)
public static void RegisterRoutes(RouteCollection routes) { routes.MapPageRoute("", "Category/{action}/{categoryName}", "~/categoriespage.aspx", true, new RouteValueDictionary {{"categoryName", "food"}, {"action", "show"}}, new RouteValueDictionary {{"locale", "[a-z]{2}-[a-z]{2}"},{"year", @"\d{4}"}} ); }
路由处理 URL 请求时,在上一示例中演示的路由定义生成下表列出的结果。
URL |
结果 |
---|---|
/US |
无匹配。 locale 和 year 都是必需的。 |
/US/08 |
无匹配。 对 year 的约束需要 4 个数字。 |
/US/2008 |
locale = "US" year = "2008" |
在某些情况下,即使为网站启用了请求,ASP.NET 路由也不处理请求。 本节介绍路由不处理请求的一些情况。
找到匹配 URL 模式的物理文件
默认情况下,路由不处理映射到 Web 服务器上现有物理文件的请求。 例如,如果 Products/Beverages/Coffee.aspx 上存在物理文件,则路由不处理对http://server/application/Products/Beverages/Coffee.aspx 的请求。 即使匹配一个定义的模式(例如 {controller}/{action}/{id}),路由也不处理该请求。
如果希望路由处理所有请求(包括指向文件的请求),可以通过将 RouteCollection 对象的 RouteExistingFiles属性设置为 true 来重写默认行为。 将该值设置为 true 后,与定义的模式匹配的所有请求都将由路由处理。
为 URL 模式显式禁用路由
还可以指定路由不应处理某些 URL 请求。 通过定义路由并指定应使用 StopRoutingHandler 类来处理该模式,来阻止路由处理某些特定请求。 当 StopRoutingHandler 对象处理请求时,StopRoutingHandler 对象会阻止以任何其他方式将该请求处理为路由。 而是会将该请求处理为 ASP.NET 页、Web 服务或其他 ASP.NET 终结点。 可以使用 RouteCollection.Ignore 方法(对于 MVC 应用程序,为RouteCollectionExtensions.IgnoreRoute)创建使用 StopRoutingHandler 类的路由。 下面的示例演示如何阻止路由处理 WebResource.axd 文件的请求。
路由在处理 URL 请求时,还尝试将请求的 URL 与路由匹配。 将 URL 请求与路由匹配取决于以下所有条件:
-
包括在项目类型中的已经定义的路由模式或默认路由模式(如果有的话)。
-
将路由添加到 Routes 集合中的顺序。
-
已经提供给路由的所有默认值。
-
已经提供给路由的任意约束。
-
是否定义路由来处理匹配物理文件的请求。
为避免错误的处理程序处理请求,必须在定义路由时考虑以上所有条件。 出现在 Routes 集合中的 Route 对象的顺序是很重要的。 路由将在集合的整个路由过程中一直尝试匹配。 当匹配发生时,无法计算更多的路由。 通常,按从路由定义的具体性递减的顺序将路由添加到 Routes 属性。
例如,假定您使用以下模式添加路由:
-
路由 1 设置为 {controller}/{action}/{id}
-
路由 2 设置为 products/show/{id}
路由 2 将不再处理请求,这是因为首先计算路由 1,路由 1 始终匹配同样适用于路由 2 的请求。 对http://server/application/products/show/bikes 的请求似乎能更好地匹配路由 2,但它是由路由 1 使用下列值处理的:
-
controller 为 products。
-
action 为 show。
-
id 为 bikes。
如果请求缺少参数,则会使用默认值。 因此,可能导致路由匹配意外的请求。 例如,假定您使用以下模式添加路由:
-
路由 1:{report}/{year}/{month},对于 year 和 month 使用默认值。
-
路由 2:{report}/{year},对于 year 使用默认值。
路由 2 将永远不处理请求。 路由 1 可能用于月度报表,而路由 2 可能用于年度报表。 但是,路由 1 中的默认值意味着将匹配同时适用于路由 2 的所有请求。
可以通过在模式中包括例如 annual/{report}/{year} 和 monthly/{report}/{year}/{month} 的常量来避免二义性。
如果 URL 与在 RouteTable 集合中定义的任何 Route 对象都不匹配,ASP.NET 路由将不处理请求。 相反,会将处理传递给 ASP.NET 页、Web 服务或其他 ASP.NET 终结点。
如果想要在站点中创建页的超链接,可以使用 URL 模式通过编程方式来创建对应于路由的 URL。 当更改模式时,将自动生成匹配新模式的 URL。 有关如何在代码或标记中生成 URL 的信息,请参见如何:通过路由构造 URL。
在已路由的页请求的处理程序中,可以使用代码或标记访问 URL 占位符中传递的值。 有关更多信息,请参见如何:在路由页面中访问 URL 参数。
对于 MVC 应用程序,MVC 框架自动处理 URL 占位符中传递的值。 有关更多信息,请参见在 ASP.NET MVC 应用程序中传递数据。
授权规则可以仅应用于路由 URL,也可以同时应用于路由 URL 和它映射到的物理 URL。 例如,授权规则可以指定所有用户可以访问以 Category 开头的 URL,但是只有管理员可以访问 Categories.aspx 页。 如果路由 URL 模式 contoso.com/Category/{controller}/{action} 映射到物理 URLcontoso.com/Categoriespage.aspx,且您仅将授权规则应用于路由 URL,则当使用路由 URL 请求 Categoriespage.aspx 时允许所有用户访问 Categoriespage.aspx。 但是,当使用物理 URL 请求它时,只有管理员可以访问它。
默认情况下,授权规则同时应用于路由 URL 和物理 URL。 有关更多信息,请参见PageRouteHandler.CheckPhysicalUrlAccess 属性。
下表列出了 ASP.NET 路由的关键服务器类。
类 |
说明 |
---|---|
表示 Web 窗体或 MVC 应用程序中的路由。 |
|
表示动态数据应用程序中的路由。 |
|
用作表示 ASP.NET 路由的所有类的基类。 |
|
存储应用程序的路由。 |
|
提供使您可以管理路由集合的方法。 |
|
提供使您可以在 MVC 应用程序中管理路由集合的其他方法。 |
|
包含所请求路由的值。 |
|
包含有关对应于路由的 HTTP 请求的信息。 |
|
提供一种方式,来指定 ASP.NET 路由不应处理 URL 模式的请求。 |
|
提供用于定义 Web 窗体应用程序的路由的方法。 |
|
提供用于存储路由 Constraints、Defaults 和 DataTokens 对象的方法。 |
|
提供用于从路由信息生成 URL 的方法。 |
ASP.NET 路由不同于 URL 重写。 URL 重写通过在将请求发送到网页之前实际更改 URL 来处理传入请求。 例如,一个使用 URL 重写的应用程序可能会将 URL 从 /Products/Widgets/ 更改为 /Products.aspx?id=4。 此外,URL 重写通常没有相应的 API 来创建基于模式的 URL。 在 URL 重写中,如果更改了 URL 模式,则必须手动更新包含原始 URL 的所有超链接。
由于 ASP.NET 路由可以从 URL 提取值,所以处理传入请求时不更改 URL。 如果必须创建一个 URL,则将参数值传递到为您生成 URL 的方法中。 若要更改 URL 模式,请在某位置更改该模式,您在应用程序中创建的基于该模式的所有链接将自动使用新模式。