《Pro ASP.NET MVC 3 Framework》学习笔记之十九【URL和Routing】

创建传出的URL(Outgoing URLs)

处理传入的URL(Incoming URLs)仅仅是路由系统的一部分功能。我们也需要使用URL架构来创建Outgoing URLs,我们可以将这些URL嵌入到我们的View里面,并且能够让用户点击URL提交表单给我们的应用程序,并能够命中合适的controller和action。

下面会展示用不同的技术来创建Outgoing URLs:

最便捷的创建Outgoing URLs就是手动定义如:<a href=”/Home/About”>About this application</a> 这样的URL。这个URL会命中HomeController中的About action方法。手动定义Outgoing URLs的确非常便捷,但是这样做也是非常危险的,因为一旦你改变了自己应用程序的URL架构,你就打破了所有手动创建的Outgoing URLs。你必须排查修改所有的View里面的Outgoing URLs,并更新所有的Controller和Action中对这些URL的引用。

Routing System能够从URL架构创建URL,因此如果URL架构发生了变化,那么由此创建在View里面的Outgoing URLs也会随之发生改变。显然这种方式才是明智之举,但也需要我们在前期做一些初始化的工作,这是非常值得的,长远来看这会让我们获益匪浅。

下面会通过实例来说明,仍然借用前面的项目,

只不过做些修改:1)移除AdditionalControllers 2)修改RegisterRoutes如下:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(“MyRoute”, “{controller}/{action}/{id}”,
new { controller = “Home”, action = “Index”, id = UrlParameter.Optional });
}
最简便的在View里面创建一个Outgoing URL就是调用Html.ActionLink方法。如:@Html.ActionLink(“关于”, “About”).该方法是根据当前的URL架构创建URL的,查看页面的源文件可以发现该URL为<a href=”/Home/About”>关于</a>。假设我们改变URL架构如下:

public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(“NewRoute”, “App/Do{action}”,
new { controller = “Home” });

routes.MapRoute(“MyRoute”, “{controller}/{action}/{id}”,
new { controller = “Home”, action = “Index”, id = UrlParameter.Optional });
}

我们会得到这样的一个URL:<a href=”/App/DoAbout”>关于</a>

理解输出的URL路由匹配

通过上面的介绍,我们已经了解了路由系统是怎么根据URL架构的改变来创建的Outgoing URL的。应用程序通常会定义若干路由,所以理解路由怎样被选中来进行URL创建就非常重要。路由系统是按照路由被添加到RouteCollection里面的顺序来处理的,RouteCollection对象是要传递给RegisterRoutes方法。对每一个路由会根据下面三个条件来进行是否匹配的检查:

1)提供的值必须对定义在URL模式里面的每一个segment变量是可用的。
为每一个segment寻找值时遵循如下规则:
首先,路由系统会在我们提供的值,也就是定义在匿名类型的属性里面的值中寻找
其次,在当前请求的变量值里寻找
最后,在我们定义路由时提供的默认值里面寻找

2)我们提供的segment变量的值可能都与定义在route里面的默认变量(default-only)一致。这些变量的默认值已经提供,但是这些变量并没有出现在我们定义的URL模式中。
例如有这样一个路由:routes.MapRoute(“MyRoute”, “{controller}/{action}”, new { myVar = “true” });其中myVar就是default-only变量,为了让这个路由匹配,
我们必须注意不要为myVar提供一个值或确保我们提供的值能够匹配默认值。

3)所有segment变量的值必须满足路由约束

要明确的是:路由系统不是寻找最佳的匹配路由,仅仅是找第一个匹配的然后用它创建URL。只有找到了一个匹配的路由,那么路由系统就会忽略后面所有的路由。因此,我们定义路由时一定按照从具体到模糊的顺序。

下面进行单元测试Outgoing URLs

[TestMethod]
public void TestOutgoingRoutes()
{
// Arrange
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
RequestContext context = new RequestContext(CreateHttpContext(), new RouteData());

// Act – generate the URL
string result = UrlHelper.GenerateUrl(null, “Index”, “Home”, null,
routes, context, true);

// Assert
Assert.AreEqual(“/”, result);
}

针对其他控制器(Targeting Other Controllers)

ActionLink方法默认是假定我们针对的是同一个Controller中的action方法,由该action方法来呈现view。如果要指定其他的Controller可以多加个参数如:@Html.ActionLink(“About this application”, “About”, “MyController”)。

传递额外的值(Passing Extra Values)

我们可以使用匿名类型的属性来传递segment变量的值。如@Html.ActionLink(“About this application”, “About”, new { id = “MyID” })。生成的URL如下:
<a href=”/Home/About/MyID”>About this application</a>

理解segment变量的重用

在我们描述路由匹配到传出的URLs时,我们提到当为每一个segment变量寻找值时,路由系统将会从当前的请求中寻找。现在我们定义这样一个路由如下:
routes.MapRoute(“MyRoute”, “{controller}/{action}/{color}/{page}”)

接着在Index视图里面定义这样一个连接:@Html.ActionLink(“点我啊”, “Index”, “Home”, new { page = 789}, null).运行程序首先输入如下URL:
http://localhost:<port>/Home/Index/Purple/123。页面呈现的链接是:<a href=”/Home/Index/Purple/789″>点我啊</a>.

我们定义链接时并没有给color赋值,而这里的Purple是怎么来的呢?

这就是重用了刚开始传入的URL:http://localhost:<port>/Home/Index/Purple/123中的color值Purple。而且路由系统是重用page之前的segment变量的值。当我们对ActionLink做下修改:@Html.ActionLink(“Click me”, “List”, “Catalog”, new {color=”Aqua”}, null);

这里我们提供了color的值,因为color在page之前,所以这里不会重用传入URL的color值。展示为:<a href=”/?color=Red”>点我啊</a>
这里强烈建议我们避免这种行为出现,而是在定义URL模式时提供你需要的所有segment变量的值。
当我们提供的值跟segment变量的值不一致时,这个值会作为querystring附加在URL的后面。

例如:@Html.ActionLink(“About this application”, “About”, new { id = “MyID”, myVariable = “MyValue” }),创建的连接显示如下:
<a href=”/Home/About/MyID?myVariable=MyValue”>About this application</a> 。

如果我们提供的值碰巧跟默认值一样,这时路由系统会忽略来自Outgoing URL的变量。例如:创建这样一个链接 @Html.ActionLink(“About this application”, “Index”, “Home”) 。显示的链接就是这样:<a href=”/”>About this application</a>

指定HTML属性

我们可以为创建的链接指定css样式,如可以指定一个样式类别为myCSSClass可以这样做:
@Html.ActionLink(“About this application”, “Index”, “Home”, null, new {id = “myAnchorID”, @class = “myCSSClass”})注意这里用了一个”@”符号,因为class是C#里面关键字,这里需要的是HTML样式类名。这样展示的<a>标签会多一个属性:<a class=”myCSSClass” href=”/” id=”myAnchorID”>About this application</a>

创建绝对路径的URL

前面创建的都是相对路径,我们还可以创建全路径如下:

@Html.ActionLink(“About this application”, “Index”, “Home”, “https”, “myserver.mydomain.com”,
“myFragmentName”,new { id = “MyId”}, new { id = “myAnchorID”, @class = “myCSSClass”}) 。

这里建议我们尽量使用相对路径。因为全路径依赖于呈现给用户的应用程序的基础架构,有太多依赖绝对路径的大的应用程序最后因为网络基础设施或域名策略的改变而被破坏,而这种变化常常是程序员无法控制的。

有时我们需要将URL作为文本显示在页面上,这是可以是用Url.Action(),用法跟Html.ActionLink类似。

根据Routing Data创建链接或URL

如@Html.RouteLink(“Routed Link”, new { controller = “Home”, action = “About”, id=”MyID”})。

在页面展示的链接如下:
<a href=”/Home/About/MyID”>Routed Link</a>

使用 Url.RouteUrl方法可以让创建的链接以文本形式显示。

在Action方法里面创建Outgoing URLs

大多数情况下,我们会在View里面创建Outgoing URLs,但有些时候我们也需要在action方法里面做类似的事情。方法一样,如下所示:

public ActionResult MyActionMethod()
{
string myActionUrl = Url.Action(“Index”, new { id = “MyId” });
string myRouteUrl = Url.RouteUrl(new { controller = “Home”, action = “About” });
return Redirect(myRouteUrl);
}

根据一个具体的Route创建URL

我们在使用routes.MapRoute()时一般传了一个名字作为参数,如下面这样的:

routes.MapRoute(“MyRoute”, “{controller}/{action}”);
routes.MapRoute(“MyOtherRoute”, “App/{action}”, new { controller = “Home” });

给route命名有如下两个理由:

1.可以用来提示该路由的作用 2.在创建Outgoing URL时可以选择具体的route

我们在使用@Html.ActionLink(“Click me”,”About”)时具体会选择那一个route呢?

答案就是:定义在RegisterRoutes()里面的第一个route。这里选择的就是MyRoute。当然我们也可以自己指定一个route,如@Html.RouteLink(“Click me”, “MyOtherRoute”, new { action = “About” });

不建议使用命名路由的情形

创建Outgoing URL依赖route命名会打破”分解关注点”(这是MVC设计模式的核心思想)。当在view或action里面创建URL或链接时,我们的关注点是用户将被定向到的action和Controller,不是要使用的URL格式。所以我们倾向于避免给route命名,而是是用null代替。

好了,今天的笔记做到这里!
周末愉快!

作者:张雪飞
出处:https://zhangxuefei.site/p/223
版权说明:欢迎转载,但必须注明出处,并在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发表评论

电子邮件地址不会被公开。 必填项已用*标注