Scott的ASP.net MVC框架系列文章之二: URL映射

[原文地址][查看该系列的其他文章]

上个月我发表了介绍ASP.net MVC框架系列文章的第一篇,介绍了一个具有产品列表/浏览功能的简单的电子商务网站,包含了MVC背后的高级概念,并演示了从零开始实现如何创建一个ASP.net MVC项目并测试了电子商务网站的产品列表功能。

在今天的这篇文章里我将要深入介绍ASP.net MVC框架中的URL映射架构,并讨论如何在你的应用程序里里用这种方式进行更高级的开发。

Part1回顾

Part1中,我们创建了一个电子商务网站并实现了一下三种URL:

URL Format

Behavior

URL Example

/Products/Categories

Browse all Product Categories

/Products/Categories

/Products/List/Category

List Products within a Category

/Products/List/Beverages

/Products/Detail/ProductID

Show Details about a Specific Product

/Products/Detail/34

我们创建了ProductController类实现对这些URL的处理:

http://www.scottgu.com/blogposts/mvc2/step11.jpg

上面的类被加入我们的应用程序后,ASP.net MVC框架自动处理URL请求并选择控制类中相应的操作方法进行处理。

今天的文章里我们将要深入探讨ASP.net MVC框架URL映射的工作机制和高级应用,我将演示如何对URL映射进行单元测试。

ASP.net MVC URL映射系统的功能

ASP.net MVC框架包含一个复杂的URL映射系统,你可以在其中自己定义映射规则,映射系统有两个主要的功能:

  1. 正确解析传入的 URL请求并定位到正确的控制类和控制方法进行处理
  2. 构建输出的用于调用控制类/操作方法的URL路径(例如:提交表单, <a href=””>链接,以及AJAX调用)

URL映射规则提供的解析传入URL和生成输出URL的功能增强了应用程序的灵活性,这意味着如果我们希望修改应用程序的URL结构(例如重命名/Products/Catalog),我们只需要应用程序级别的修改映射规则而不需要修改控制类或视图模板中的任何代码。

ASP.net MVC 的默认URL映射规则

当你用VisualStudio利用模板创建ASP.net MVC项目时,Global.asax文件的后台代码中自动添加了一个ASP.net Application类:

http://www.scottgu.com/blogposts/mvcrouting/step1.jpg

该应用程序类是开发人员可以添加应用程序启动/停止以及全局错误处理逻辑。

ASP.net MVC项目模板默认在Application类中自动添加Application_Start方法,并在其中注册两条URL映射规则:

http://www.scottgu.com/blogposts/mvcrouting/step2.jpg

上面的第一条路由规则表明ASP.net MVC框架默认按照”[Controller]/[action]/[id]”的格式确定调用哪一个控制类实例和相应的操作方法来处理请求(同时指明了传入的参数)。

该默认映射规则使Part 1部分提到的电子商务程序中/Products/Detail/3的请求调用ProductController类中的Detail方法并传入3作为方法参数:

http://www.scottgu.com/blogposts/mvc2/step10.jpg

前面添加的第二个映射规则用于处理应用程序中的默认页”Default.aspx”问题(有时服务器是通过”/”方式请求默认页的)。这条规则使得对于”Default.aspx””/”方式的请求都可以被HomeController类的Index()方法处理(HomeController类在使用Visual Studio根据模板创建ASP.net MVC应用程序时被默认自动创建)

理解映射实例

映射规则是通过向System.Web.Mvc.RouteTable中的映射集合(Routes Collection)中注册添加新的映射实例(Route Instatnces)完成的。

Route类定义了很多用于配置映射规则的属性,你可以通过.net 2.0“传统”的属性赋值方式进行赋值:

http://www.scottgu.com/blogposts/mvcrouting/step4.jpg

也可以通过使用VS2008 C#VB编译器中提供的object initializer特性进行赋值操作:

http://www.scottgu.com/blogposts/mvcrouting/step3.jpg 

Route类的“Url”属性定义了已被启用并对当前请求进行处理的Url匹配规则,同时定义了如何解析URL中传入的参数,[ParamName]定义了Url中可以变换的参数名称,后面我们将会看到,我们并非只局限于使用已知的常用参数名称,你可以在URL中使用任何参数名称,例如,我使用如下一条规则"/Blogs/[Username]/Archive/[Year]/[Month]/[Day]/[Title]"处理针对Blog文章的请求,并使MVC框架自动解析传入UserName, Year, Month, DayTitle变量,传入控制类方法中。

Route类的”Defaults”属性定义了一系列当传入URL中不包含某一指定参数值情况下使用的默认值,例如,在上面的URL映射例子中我们为”[action]””[id]”分别定义了两个URL参数默认值,这意味着如果应用程序接收到/Products/的请求,映射引擎将会默认调用ProductController类中的Index方法,同理,如果接收到/Products/List的请求,则null值作为ID参数的默认值。

Route类的”RouteHandler”属性定义了当URL被相应的的URL映射规则匹配结束后用于进一步处理请求的IRouteHandler实例,在上面的例子中,我们指明了使用System.Web.Mvc.MvcRouterHandler类处理URL请求。这样做的目的是保证URL映射引擎可以处理MVC和非MVC的请求。使用IRouteHandler接口意味着我们也可以处理非MVC请求(例如标准WebForm, Astoria REST支持等等)。

Route类还有一个”Validation”属性,后面的文章中我们将看到这个属性的用处,这个属性允许我们指定根据某一规则进行匹配之前需要符合的先决条件,例如,我们可以指定某条映射规则只对某一个HTTP Verb(这让我们可以很轻松的映射REST命令),或者我们可以在参数中使用一个正则表达式确定某条映射规则是否确实匹配。

注意:在MVC的第一个预览版本中Route类是不可扩展的(相反它是一个数据类),在以后的版本中我们将使他可以uozhan并允许开发人员根据需要添加特殊的Route类(例如:RestRoute子类)扩展功能。

判断映射规则

ASP.net MVC Web应用程序接收到URL请求时,MVC框架判断RouteTable.Routes集合中的规则并确定使用哪个控制类处理该请求。

MVC框架根据RouteTabe中映射规则的注册顺序选择对应的控制类,URL请求与每条映射规则进行匹配,如果匹配成功则更该规则及其RouteHandler将用于处理请求(其他的规则将被忽略)。这意味着你应该按照匹配条件由多到少的顺序安排你的映射规则。

映射应用:自定义搜索URL

让我们通过实现电子商务网站的检索功能来在看一与实际应用相应的自定义映射规则:

首先我们在项目中添加一个SearchController类:

http://www.scottgu.com/blogposts/mvcrouting/step5.jpg

然后我们在SearchController类中定义两个操作方法。Index()方法用于显示包含一个用于接收用户输入的文本框和一个提交按钮的检索页面,Result()方法用于处理表单数据,并对数据库执行查询和将结果显示给用户。

 http://www.scottgu.com/blogposts/mvcrouting/step6.jpg

使用默认的/[controller]/[action]/[id]规则,我们使用下面的URL调用SearchController的操作方法:

Scenario

URL

Action Method

Search Form:

/Search/

Index

Search Results:

/Search/Results?query=Beverages

Results

 

/Search/Results?query=ASP.NET

Results

注意因为默认添加的/[controller]/[action]/[id]规则将Index()方法制定为控制类的默认方法(在”Defaults”属性中制定),所以根路径/Search被默认映射到Index()方法中:

http://www.scottgu.com/blogposts/mvcrouting/step3.jpg

诸如/Search/Results?query=BeveragesURL概括了我们期望的功能,但同时我们也希望可以让搜索结果页具有一个更友好的URL,因此我们希望从URL中排除“Results”这一操作方法名称,并把查询关键字包含在Url中而不通过QueryString参数传递,例如:

Scenario

URL

Action Method

Search Form:

/Search/

Index

Search Results:

/Search/Beverages

Results

 

/Search/ASP.NET

Results

我们可以在默认的/[controller]/[action]/[id]规则前添加自定义的URL映射规则来让搜索结果页具备更友好的URL:

http://www.scottgu.com/blogposts/mvcrouting/step7.jpg

我们声明的前两条规则指明了/Search/ 路径的控制类和操作方法参数,我们指明/SearchSearchControllerIndex()方法处理,而诸如/Search/Foo, /Search/BarURL总是有SearchControllerResult方法处理。

上面的第二条映射规则指明路径/Search/前缀厚的部分都被作为”[query]”参数的值,并传给SearchController类中的Result方法:

http://www.scottgu.com/blogposts/mvcrouting/step8.jpg

我们同时希望对检索结果进行分页处理(每次只显示10条数据),我们可以通过QueryString来作为参数(例如/Search/Beverages?page=2),或者可以把页码作为URL路径的一部分(例如:/Search/Beverages/2)。为了实现这个特性,我们只需要在第二条映射规则中添加一个可选参数:

http://www.scottgu.com/blogposts/mvcrouting/step9.jpg

注意新的URL映射规则是“Search/[query]/[page]”,我们同时配置了当路径中不包含页码时的默认页索引为1(通过anonymous type给“Defaults”属性传递值)

我们可以更新SearchController.Results()方法把页码参数作为方法变量。

http://www.scottgu.com/blogposts/mvcrouting/step10.jpg 

通过这种方式,我们的站点的搜索部分可以拥有更加友好的URL路径(剩下的就是去实现检索逻辑了,这部分作为留给读者的练习)。

映射规则先决条件的验证

前文中曾经提到Route类中含有一个Validation属性,允许你设置匹配映射规则的先决条件。ASP.net MVC框架允许你使用正则表达式验证URL中的参数和分析HTTP Headers(根据不同的Verbs执行不同的映射规则)

下面是一个可以用于类似于”/Products/Detail/43”的路径的验证规则。他指明了ID参数必须是一个数字(不允许字符出现),同时该参数必须是1-8个字符长:

http://www.scottgu.com/blogposts/mvcrouting/step11.jpg

如果我们对应用程序发出/Products/Detail/12的请求,符合上面的映射规则,如果我们传入/Products/Detail/abc,或者/Products/Detail/23232323232,则不符合该规则。

从映射系统中构造URL

前面提到了ASP.net MVC框架的映射系统(Routing System)负责两项任务:

  1. 将传入的URL请求交由相应的控制类、操作方法进行处理
  2. 构建可以用于定位到控制类、操作方法的URL路径(例如:form posts, <a href=””>链接,AJAX调用等)

URL映射系统提供哦你了一些辅助方法和类,使得在运行时可以动态查找和构建URL(你也可以通过直接操作RouteTable的内容进行查找)。

Html.ActionLink

Part 1中我曾经提到Html.ActionLink()视图辅助方法。它可以用在视图中动态产生<a href=””>链接。而且它可以根据映射系统中定义的URL映射规则产生URL,例如下面的两个HtmlActionLink调用:

http://www.scottgu.com/blogposts/mvcrouting/step12.jpg

该方法自动选择我们在前边定义的对应的Search结果映射规则,并将href属性自动设置为:

http://www.scottgu.com/blogposts/mvcrouting/step13.jpg

需要注意的是上面对Html.ActionLink的第二个调用自动将“page”参数作为URL的一部分(同时注意第一个调用略去了该参数,因为服务器端将会提供一个默认值)。

Url.Action

除了Html.ActionLink以外,ASP.net MVC还提供了Url.Action()视图辅助方法,该方法可以产生你希望使用的任何原始字符串URL,看一下下面的代码:

http://www.scottgu.com/blogposts/mvcrouting/step14.jpg

该方法根据URL映射系统返回下面的原始 URL(没有用<a href=””>元素包装):

http://www.scottgu.com/blogposts/mvcrouting/step15.jpg

Controller.RedirectToAction

ASP.net MVC同样支持在控制类中使用Controller.RedirectToActioin()辅助方法实现重定位(其中URL根据映射系统产生)。

例如下面在控制类中的调用代码:

http://www.scottgu.com/blogposts/mvcrouting/step16.jpg 

该方法在内部产生了一个到Response.Redirect(“/Search/Beverages”)的调用。

DRY

上面提到的辅助方法都有共同的优点就是避免我们把URL路径因编码到控制类和视图逻辑中,因此如果以后我们想把检索路径的URL映射规则从“/Search/[query]/[page]”改回“/Search/Results/[query]/[page] /Search/Results?query=[query]&page=[page]”我们只需要在映射规则注册的地方进行修改。我们不需要修改任何存在于控制类和视图中的方法来定位到新的URL(这符合了“DRY法则)

根据映射系统产生URL路径(使用Lambda表达式)

前面的URL辅助方法展示了在C#VBVS2008中支持的anonymous type类型的优势,在上面的例子中,我们通过使用anonymous type传入一系列name/value以辅助产生URL(你可以把它当成一种产生词典的快捷方式)

除了通过使用anonymous类型动态传递参数,ASP.net MVC框架同样支持通过使用可以提供编译时检查的强类型机制创建操作方法映射,该功能通过泛型和VBC#Lambda表达式的支持实现。

例如,下面的anonymous类型ActionLink调用:

http://www.scottgu.com/blogposts/mvcrouting/step17.jpg

也可以被写作:

http://www.scottgu.com/blogposts/mvcrouting/step18.jpg

第二种方式除了书写更加鉴别,同时具备类型安全的优点,这意味着你可在开发时可以得到Visual Studio提供的智能感知和编译时错误检查支持(你也可以针对它是哟哦那个映射工具):

http://www.scottgu.com/blogposts/mvcrouting/step19.jpg

注意上面我们可以使用智能感知功能取得SearchController类的Action()方法的,同时参数也都是强类型的,产生的URL路径是受映射系统驱动的。

你或许想知道,这些是如何实现的,如果你仍然记得8个月前我写的关于Lambda表达式的文章,该文章中提到Lambda表达式既可以编译成为代码委托,也可以作为运行时分析Lambda表达式的an expression tree object。通过Html.ActionLink<T>辅助方法,我们可以使用expression tree选项并在运行时分析lambda表达式并查找表达式中指定的参数类型,名称和值。我们可以在URL映射系统中使用它产生相应的URL和相应的HTML

重要:当使用Lambda表达式时,我们并没有真正的执行Controller的方法,例如:下面的代码并没有调用SearchController中的”Results”方法:

http://www.scottgu.com/blogposts/mvcrouting/step18.jpg

相反它直接生成了HTML链接:

http://www.scottgu.com/blogposts/mvcrouting/step20.jpg

当用户点击这个链接时,它将产生一个调用SearchControllerResults方法的请求。

单元测试映射

ASP.net MVC框架的核心设计规范对测试提供了良好的支持,例如你可以轻松的对映射规则进行单元测试。MVC映射系统可以被实例化并独立于ASP.net而运行,这意味着你可以使用任何单元测试库(例如Nunit, MBUnit, MSTest等)加载映射模块并进行测试。

尽管你可以直接对ASP.net MVC应用的程序的全局映射表进行单元测试,然而一般来讲让单元测试依赖于全局状态变量并不是一个好主意。而另一种你可以使用的更好的方式是将你的映射规则注册逻辑封装到一个RegisterRoutes()辅助方法中,像下面的代码作为参数传入的RouteCollection(在以后的版本中我们可能会把这种方式作为Vs的默认模式)

http://www.scottgu.com/blogposts/mvcrouting/step21.jpg

这样你就可以编写单元测试创建独立的RouteCollection实例并调用应用程序的类的RegisterRoutes()方法将映射规则注册到应用程序中。你可以模拟各种请求并验证是否返回了正确的控制类和操作方法,而不必担心其他影响:

http://www.scottgu.com/blogposts/mvcrouting/step22.jpg

总结

希望这篇文章对ASP.net MVC的映射机制的工作原理以及在ASP.net MVC应用程序中设计URL的结构有了更加深入的了解。

当你创建ASP.net MVC Web程序时,一条默认的映射规则/[controller]/[action]/[id]被自动注册(不需要你进行任何手动配置),这已经足够你开发很多应用。但是希望这篇文章已经向你演示了如何简单的自定义URL规则,以及MVC框架在这方面提供的强大功能和灵活性。

希望对你有所帮助,

Scott

发布了10 篇原创文章 · 获赞 1 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览