10.6 URL路由
学习目标
了解URL路由的概念及作用。
本节,会结合10.4节的例子,深入分析ASP.NET MVC应用程序中URL路由的概念以及URL路由在ASP.NET MVC 4 Web程序中的作用。
10.6.1 一个URL并不等同于一个页面
当创建一个传统的ASP.NET Web窗体应用程序,或者是一个动态服务器页面(ASP)应用程序时,在URL与页面之间存在一一对应的关系。如果请求服务器上的一个名为abc.aspx的页面,那么服务器对应磁盘上就会有一个abc.aspx页面。如果abc.aspx文件在服务器上不存在,开发人员将会获得“404-页面不存在错误”。
当创建一个ASP.NET MVC应用程序时则大不相同,输入到浏览器地址栏的URL与应用程序中的文件之间并没有一个对应关系。在一个ASP.NET MVC应用程序中,一个URL与一个控制器的动作(Action)相对应,而不是磁盘上的具体页面。
例如,在MVC 4 Web程序中访问“/Home/Index”地址,就是访问Home Controllers这个控制器类的Index方法,方法可能直接在客户端浏览器中输出结果,也可能会调用控制器对应的视图来呈现在客户端,具体就看HomeControllers控制器中的方法是如何定义的。
10.6.2 理解URL路由
URL路由(URL Routing)使用一张路由表处理到来的请求。这个路由表在MVC 4 Web应用程序首次启动时创建在RouteConfig.cs这个文件中。
而在系统全局文件Global.asax中通过在Application_Start()事件中的"RouteConfig.RegisterRoutes(RouteTable.Routes);"调用,在Web应用程序启动时来创建路由。系统默认创建的RouteConfig.cs源代码如下。
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new{controller="Home",action = "Index",id = UrlParameter.Optional } ); } }
很显然,默认的路由表只含有一个路由。这个默认的路由将所有到来的请求分为了3个分段(一个URL分段是正斜杠之间的任何内容)。第一个分段映射到了控制器名称,第二个分段映射到了动作名称,最后一个分段映射到了传递给动作的名为Id的参数。
例如,有URL:/Product/Details/5,这个URL将会被解析为如下3部分:
Controller = ProductController
Action = Details
Id = 5
默认的路由包含所有3个分段的默认值。默认的控制器是HomeController,默认的动作是Index,默认的Id是一个空字符串。
再比如有URL:/Employee,这个URL将被解析为如下3个参数:
Controller = EmployeeController
Action = Index
Id = ""
最后,如果打开一个ASP.NET MVC应用程序而不提供任何的URL(例如,http://localhost),然后这个URL将会被解析成如下3个参数:
Controller = HomeController
Action = Index
Id = ""
这个请求被发送到HomeController类的Index()动作。
10.6.3 创建自定义路由
对于简单的ASP.NET MVC 4 Web应用程序,默认的路由表已经可以很好地完成工作了。然而,很多时候还是需要创建一个自定义路由。
比如,在ProductController控制器中添加一个动作方法,代码如下。
public string Detail(int id) { return id.ToString ()+".html显示的详细内容..."; }
按照默认URL路由配置,就只能以“/Product/Detail/53”来访问。如果希望用“/prod/detail-53”来访问该方法,就可以在RouteConfig.cs文件中,增加新的路由配置来完成,此时完整的RouteConfig.cs源代码如下。
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name:"product_detail_route", url:"prod/detail-{id}", defaults:new{controller="Product",action ="Detail",id = UrlParameter.Optional } ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults:new{controller="Home",action= "Index",id = UrlParameter.Optional } ); } }
必须注意,添加到路由表中的路由顺序非常重要。新自定义product_detail_route路由在现有的Default路由前面。如果将这个顺序颠倒过来,那么Default路由将总是被调用,而从来不会调用自定义路由product_detail_route。
如此定义后,就可以“/prod/detail-53”的URL来访问ProductController中的Detail方法,访问“/prod/detail-53”运行结果如图10-18所示,当然如果给Detail方法返回了视图,页面就会返回对应视图的输出效果,前提就是要在Views文件夹中定义对应的视图文件。
10.6.4 创建路由约束
使用路由约束来限制匹配特定路由的浏览器请求。可以使用正则表达式来指定一个路由约束。
以上小节自定义的路由为例来说明。
routes.MapRoute( name:"product_detail_route", url:"prod/detail-{id}", defaults:new{controller="Product",action= "Detail",id = UrlParameter.Optional } );
代码中定义了一个名称为product_detail_route的路由,注意到Product控制器公布的Detail()动作接受一个称为id的参数。这个参数是一个整数参数。于是,路由会匹配下面的任意URL:“/prod/detail-23”,“/prod/detail-abcd”,但是由于Detail()动作期望的是一个整数值,发起一个含有非整数值的请求将会导致错误。如果在浏览器中输入“/prod/detail-abcd”的URL,那么将会得到如图10-19所示的错误页。
实际想做的是只匹配包含合适整数id的URL。当自定义路由限制与路由相匹配的URL时,就可以使用约束定义。下面的代码就是修改后的product_detail_route路由,它包含一个正则表达式来限制只匹配数字。
routes.MapRoute( name:"product_detail_route", url:"prod/detail-{id}", defaults: new { controller = "Product", action = "Detail", id = UrlParameter.Optional }, constraints: new { id = @"\d+" } );
此时,再访问“/prod/detail-abcd”的URL,那么将会得到如图10-20所示的错误页,这是比较合适的错误提示页。