概述
使用MVC Routing可以不必将URL映射到网站中特定文件。通过添加Routing规则,使URL能够安装指定的规则发送HTTP请求,这种方式能够简洁、方便、有效、快速的实现对指定页面的访问或者参数的传递,更易于被用户理解该URL
MVC Routing之优
在不使用路由的 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 片断将被解释为应用程序中一个文件的路径。
关于Routing的详细介绍请移步
URL 如何与路由匹配
路由在处理 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 终结点。
向MVC程序中添加路由
如果你想通过创建派生自 ControllerBase 类的类,并赋予它们以“Controller”结尾的名称来采纳实现控制器的 MVC 实例,此时您就不需要在 MVC 应用程序中再手动添加路由规则。 因为MVC程序已经预先配置了默认的路由规则,该规则将调用您在Controller类中实现的操作方法。
如果您想要在 MVC 应用程序中添加自定义路由,则要使用 MapRoute(RouteCollection, String, String) 方法,不要使用 MapPageRoute(String, String, String) 方法。
RouteCollectionExtensions.MapRoute 方法 (RouteCollection, String, String)
映射指定的 URL 路由。参数(应用程序的路由的集合,要映射的路由的名称,路由的 URL 模式)返回类型为System.Web.Routing.Route对映射路由的引用
RouteCollection.MapPageRoute 方法 (String, String, String)
提供用于定义 Web 窗体应用程序的路由的方法。参数(路由的名称,
路由的 URL 模式,路由的物理 URL)
提供此方法是为了方便编码。它等效于调用 Add 方法,然后传递使用 PageRouteHandler 类创建的 Route 对象。
下面的示例演示在 Global.asax 文件(该文件是在 MVC 应用程序的 Visual Studio 项目模板中定义的)中创建默认 MVC 路由的代码。
publicstaticvoid RegisterRoutes(RouteCollection routes) { //忽略对.axd文件的Route,也就是和WebForm一样直接去访问.axd文件 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller ="Home", action ="Index", id = UrlParameter.Optional } // 参数默认值 ); } protectedvoid Application_Start() { //在程序启动的时候注册我们前面定义的Route规则 RegisterRoutes(RouteTable.Routes); } }
设置URL参数默认值
定义路由时可以为参数分配一个默认值。 如果 URL 没有包括该参数的值,则会使用默认值。 通过将字典对象分配给 Route 类的 Defaults 属性,可以设置路由的默认值。 下面的示例演示如何使用 MapPageRoute(String, String, String, Boolean, RouteValueDictionary) 方法添加具有默认值的路由。
如下:MVC 应用程序的 Visual Studio 项目模板中默认 MVC 路由的代码
publicstaticvoid RegisterRoutes(RouteCollection routes) { //忽略对.axd文件的Route,也就是和WebForm一样直接去访问.axd文件 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller ="Home", action ="Index", id = UrlParameter.Optional } // 参数默认值 ); } protectedvoid Application_Start() { //在程序启动的时候注册我们前面定义的Route规则 RegisterRoutes(RouteTable.Routes); } }
RouteCollection.MapPageRoute 方法 (String, String, String, Boolean, RouteValueDictionary)
提供用于定义 Web 窗体应用程序的路由的方法。参数(路由的名称,路由的 URL 模式,路由的物理 URL,一个值 该值指示 ASP.NET 是否应验证用户是否有权访问物理 URL(始终会检查路由 URL),路由参数的默认值)
返回类型为System.Web.Routing.Route将添加到路由集合的路由
自定义默认路由
如下代码:
publicstaticvoid RegisterRoutes(RouteCollection routes) { //忽略对.axd文件的Route,也就是和WebForm一样直接去访问.axd文件 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapPageRoute("", "Category/{action}/{categoryName}", "~/categoriespage.aspx", true, new RouteValueDictionary { { "categoryName", "food"}, { "action", "show"}}); }
运行上面示例中演示的路由定义(使用针对 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 是否具有比模式中更多的段,是否将额外的段视为最后一个段的一部分。 若要以此方式处理额外的段,可以用星号 (*) 标记最后一个参数。 该参数称为“可用于放置各种信息的”参数。 具有全部捕捉参数的路由也将与那些不包含最后一个参数的任意值的 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 方法检查参数值是否有效。 总是将正则表达式视为不区分大小写。
提供 IRouteConstraint 对象后,ASP.NET 路由将通过调用 IRouteConstraint 对象的 Match 方法检查参数值是否有效。 Match 方法返回一个布尔值,该值指示参数值是否有效。
通过使用正则表达式可以规定参数格式,比如controller参数只能为4位数字:
new { controller =@"\d{4}"}
通过第IRouteConstraint 接口目前可以限制请求的类型.因为System.Web.Routing中提供了HttpMethodConstraint类, 这个类实现了IRouteConstraint 接口. 我们可以通过为RouteValueDictionary字典对象添加键为"httpMethod", 值为一个HttpMethodConstraint对象来为路由规则添加HTTP 谓词的限制, 比如限制一条路由规则只能处理GET请求:
httpMethod =new HttpMethodConstraint( "GET", "POST" )
完整的代码如下:
routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的URL new { controller ="Home", action ="Index", id ="" }, //参数默认值 new { controller =@"\d{4}" , httpMethod =new HttpMethodConstraint( "GET", "POST" ) } );
当然我们也可以在外部先创建一个RouteValueDictionary对象在作为MapRoute的参数传入, 这只是语法问题.
示例:演示如何使用 MapPageRoute 方法创建具有约束的路由,该约束限制可在 locale 和 year 参数中包括的值。 (在 MVC 应用程序中,使用 MapRoute 方法。)
publicstaticvoid 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" |
publicstaticvoid RegisterRoutes(RouteCollection routes) { //忽略对.axd文件的Route,也就是和WebForm一样直接去访问.axd文件 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); // URL匹配 // Details/list/CQ/100,200-3 routes.MapRoute( "Details", "Details/{action}/{city}/{price}-{star}", new {controller ="Details", action ="list", city ="CQ", price ="-1,-1", star ="-1"}, new {city =@"[a-zA-Z]*", price =@"(\d)+\,(\d)+", star ="[-1-5]"} ); // URL匹配 // Details/所有匹配 routes.MapRoute( "Details", "Details/{*values}", new { controller ="Details", action ="default", id ="" } ); // URL匹配 // Home/. routes.MapRoute( "Home", "{*values}", new { controller ="Home", action ="index" } ); }
在某些情况下,即使为网站启用了请求,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 文件的请求。
//忽略对.axd文件的Route,也就是和WebForm一样直接去访问.axd文件 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");