至此为止,我们一直在使用ASP.NET MVC新项目随带的默认路由配置。现在我们将深入探讨路由系统,并学习如何创建应用程序的自定义路由,以确保URL既是用户友好又是搜索引擎可访问的。

路由的全部内容都是关于URL以及如何将URL作为应用程序的外部输入的。当使用其他开发工具,如PHP、Web Form或是经典的ASP时,URL通常对应于磁盘上的物理文件。一个http://example.com/Products.aspx这样的URL会导致执行负责处理该请求的名为Products.aspx的文件。

通过使用URL路由,ASP.NET MVC解除了URL与物理文件的耦合。路由提供了一种把无扩展名的URL映射到控制器动作的方式,让开发人员对URL方案有完全的控制。

在本章中,我们将介绍路由概念及其与MVC应用程序的关系,还将简要介绍它们如何用于ASP.NET Web Form项目。我们将考察如何设计应用程序的URL方案,然后将这些概念运用于创建一个示例应用程序的路由。最后看看如何测试路由,以确保它们按预期工作。

一、介绍URL路由

与将URL绑定到磁盘上的物理文件不同,ASP.NET MVC引入的URL路由的底层结构能够将URL映射到控制器动作,而无须在服务器上有物理文件作为URL的目标。在本节中,我们将考察新建MVC项目随带的默认路由的结构,以及这些路由是如何与控制器和动作的概念相关联的。

1.默认路由

当创建一个新的ASP.NET MVC应用程序时,默认的项目模板会在Global.asax文件中调用一个名称为RegisterRoutes的方法。该方法负责为应用程序配置路由,并定义了最初两条路由——一条忽略路由和一条遵循{controller}/{action}/{id}模式的默认路由,如下所示。

路由是通过调用MapRoute方法而定义的,该方法有个过载。在这个例子中,默认路由是通过调用三个参数的过载来配置的。第一个参数是路由名("Default")。第二个参数是用来匹配URL的URL模式。本例中,URL模式被定义为具有三个片段——控制器、动作和ID。第三个参数是一个匿名类型,它为这些片段定义了默认值。

如果用户访问http://example.com/users/edit/5这样的URL,这将匹配默认路由,因为它有三个片段,如图所示。

在这个例子中,字符串users映射到controller参数,edit映射到actio,而5映射到id。由于这个URL显然与路由相匹配,MVC框架会尝试查找名称为UsersController的类、调用Edit方法,并为其id参数传递5。如果找不到控制器或动作,框架会产生一个404错误。

添加到路由定义中的默认参数意味着URL不必精确地匹配三片段URL模式。如果指定了默认控制器Home以及默认动作Index,当控制器片段省略时,路由将默认控制器为HomeController。同样,如果动作片段未指定,则路由会默认寻找Index动作。Id参数的默认值UrlParameter.Optional,意指不论是否指定第三个片段,路由都可以被匹配。下表给出了几个能匹配默认路由的例子。

URL 路由参数 被选中的动作方法
http://example.com/Users/Edit/5 Controller=User, Action=Edit, id=5 UsersController.Edit(5)
http://example.com/Users/Edit Controller=User, Action=Edit UsersController.Edit()
http://example.com/Users Controller=User, Action=Index UsersController.Index()
http://example.com Controller=User, Action=Index HomeController.Index()

在IgnoreRoute方法中,模式{resource}.axd/{*pathInfo}确保文件扩展名为.axd的任何URL不会被路由引擎所处理,这样才能确保任何自定义HTTP处理程序(其扩展名为.axd)以正确方式呗处理,而不会被路由引擎拦截。

2.入站与出站路由

入站路由(Inbound Routing):将URL映射到控制器或动作及任何附加参数。

出站路由(Outbound Routing):通过一组给定的路由数据(通常是控制器和动作)生成相应的URL。

上图所示的入站路由描述了一个控制器动作的URL调用。HTTP请求进入ASP.NET管道,并通过ASP.NET MVC应用程序注册的路由进行发送。每个路由都有处理请求的机会,而匹配路由随后会指定被使用的控制器和动作。

二、设计URL模式(schema)

1.建立简单、整洁的URL

传统URL:http://example.com/eventmanagement/events_by_month.aspx?year=2011&month=4

使用路由系统的URL:http://example.com/events/2011/04

这种URL带来的好处是,其中的日期有了一种明确的层次格式。

2.建立可破解的URL

在设计URL方案时,考虑最终用户为了改变所显示的数据要如何操纵或“破解”URL是有价值的。例如,也许可以合理地假设,从以下URL移去参数“04”,可能表示2011年发生的全部事件:

http://example.com/events/2011/04

同样的逻辑可以形成下表所示的更全面的路由列表。

URL 描述
http://example.com/events 显示全部事件
http://example.com/events/<year> 显示某年事件
http://example.com/events/<year>/<month> 显示某月事件
http://example.com/events/<year>/<month>/<day> 显示某日事件

让URL模式具有这种灵活性是很棒的,但这可能会导致应用程序中具有大量潜在的URL。在建立应用程序视图时,你总是要改出相应的导航。记住,可能在各个页面上不必对每个可能的URL组合都包含一个链接。在用户试图破解URL并使其生效时,让用户有一些惊喜的发现反而是件好事。

如果不希望用户破解,可考虑使用连接字符来替代斜线,如/events/2008-04-01。

3.使用URL参数区分请求

让我们对此路由加以扩展,并允许按类别列出事件。从用户的观点来看,最有用的URL可能像这样:

http://example.com/events/aspnet-usergroup-meeting

但现在有问题了!我们已经有了一个与/events/<something>形式匹配的路由,用来列出特定年、月、日的事件,那么现在如何用/events/<something>也匹配类别?第二个路由片段现在意味着完全不同的含义,这与现有的路由不协调了。如果把这种URL交给路由系统,它应该把这种参数可能作类别还是日期?
      幸运的是,ASP.NET MVC的路由系统允许我们运用条件,使用正则表达式来确保路由只与某个模式的参数相匹配就够了。这意味着我们可以只用一条路由,就能让/events/2011-01-01形式的请求传递给按日期显示事件的动作,而让/events/asp-net-mvc-in-action形式的请求传递给按类别显示事件的动作。

4.尽可能避免暴露数据库ID

一个用于托管开发人员事件的网站可能会定义这样的URL:

http://example.com/events/87

87是从数据库获得的每一个对象都有一个主键形式的唯一标识符,但是除了数据库管理员之外,数字87对任何人都毫无意义。因此,应该尽可能避免在URL中使用数据库生成的ID,尽量让它们有意义、可读、易于理解。

http://example.com/events/houstonTechFest2010

5.考虑添加多余信息

如果必须在URL中使用数据库ID,可考虑添加除了使URL可读外没什么目的的附加信息。

http://example.com/events/houstonTechFest2010/session-87
http://example.com/events/houstonTechFest2010/session-87/an-introduction-to-mvc

--------------------------------------------------------------------

搜索引擎优化(SEO)

当涉及网站的搜索引擎优化方面时,有必要提一提设计良好的URL的价值,在URL中放置一个相关的关键字提升搜索引擎排序。设计要点如下:

  • 为控制器和动作使用描述性的、简单的、普遍使用的单词。力求尽可能相关并使用可能用于所建页面的关键词。
  • 当在路由中包含文本参数时,用连接字符替换所有的空格符。
  • 去掉字符串参数中不重要的标点和不必要的文本。
  • 在URL可能的地方包含附加的、有意义的信息,如标题和描述等。

----------------------------------------------------------------------

三、在ASP.NET MVC中实现路由

默认项目模板创建了两个默认路由,但你可以不接受这两个默认路由的限制,添加自己的路由,以实现完全自定义的URL模式。以下将对此加以演示,以一个简单的在线商店为例,实现几个路由。我们将考察如何创建简单、静态的路由,以及创建更复杂的使用参数的路由和全匹配路由。

1.在线商店的URL模式(重要)

路由号 URL 描述
1 http://example.com/ 首页,重定向到分类列表
2 http://example.com/privacy 显示包含网站私有策略的静态页面
3 http://example.com/products/<productcode> 显示相应产品代码的产品详情页面
4 http://example.com/products/<productcode>/buy 将相应产品添加到购物篮
5 http://example.com/basket 显示当前用户的购物篮
6 http://example.com/checkout 启动当前用户的结算过程

注意:路由4中的URL不是设计给用户看的,它通过表单递交进行链接,在动作处理完成后会立即进行重定向,因而这种URL不会在地址栏中出现。

2.添加自定义静态路由

路由1是由默认路由处理的。

路由2,是一个纯静态路由,将http://example.com/privacy映射到HomeController的Privacy动作。

routes.MapRoute("privacy_policcy","privacy", new { controller="Home", action="Privacy"});

警告:添加到路由表中的路由次序决定了查找匹配时的路由搜索顺序。这意味着,源代码中列出的路由,应当从带有最具体条件的最高优先级降低到最低优先级,或全匹配路由。

3.添加自定义的动态路由

当有少量偏离一般规则的URL时,静态路由是有用的。如果路由包含与页面显示的数据相关的信息,就需要动态路由了。

路由3和路由4是用两个路由参数实现的:

routes.MapRoute("Product", "products/{productCode}/{action}", new { controller="Catalog", action="Show"});

两个占位符将匹配URL中用斜线分隔的片段,productCode参数是必需的,但action是可选的。如果action未指定,该路由会默认指向CatalogController上的Show动作,并传递productCode参数。详细代码如下。

public class CatalogController : Controller
{
private ProductRepository _productRepository = new ProductRepository(); public ActionResult Show(string productCode)
{
var product = _productRepository.GetByCode(productCode); if (product == null)
{
return new NotFoundResult();
}
return View(product);
} }

实现一个执行时能生成HTTP 404的自定义动作结果:

public class NotFoundResult:ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.StatusCode = ;
new ViewResult { ViewName = "NotFound" }.ExecuteResult(context);
}
}
}

NotFoundResult通过集成ActionResult,在其中提供了必须实现的ExecuteResult方法。该方法将响应状态码设置为404,然后渲染一个名为NotFound的视图,该视图位于Views/Shared目录。

注意:HttpNotFoundResult动作,也将响应转台码设置为404,但它未提供显示自定义错误页面的机制,因此总是会给最终用户显现一个空屏。

最后,我们可以添加模式中的路由5和路由6。

routes.MapRoute{"catalog", "{action}",
new { controller="Catalog" },
new { action=@"basket|checkout"});

这些路由几乎是静态路由,只不过它们是用一个参数和一个路由约束来实现的,以保持较少的路由数目。这么做的主要原因有两个。第一,每个请求都必须扫描路表进行匹配,所以大的路由集合会影响到性能。第二,路由越多,路由优先级问题出现的风险也越高。较少数目的路由规则更易于维护。

MapRoute方法的第四个参数包含了路由约束。约束参数是一个匿名类型形式的字典,可以用于指定如何约束特定的路由参数。在本例中,我们使用了一个正则表达式来指明,仅当片段字符串为“basket”或“checkout”时,才匹配action参数。这种约束能够适当地阻止把未知动作传递给控制器。

4.全匹路由

我们现在已经添加了静态和动态路由,以便为网站的不同URL提供内容。但假设有一个与所有路由都不匹配的请求,会发生什么?结果会抛出一个异常,这是实际应用程序中不希望发生的事情。为了对此加以处理,我们可以使用与ASP.NET的错误处理基础架构结合在一起的全匹配路由。

我们将添加一个全匹配路由,用它匹配尚未被其他路由匹配的任何URL,显示HTTP404的错误消息,它应该是最后一条被定义的路由。

routes.MapRoute("404-catch-all", "{*catchall}",
new { controller="Error", action="NotFound"});

值catchall为全匹配路由要拾取的值提供了一个名称。与规则路由参数不同,全匹配参数(以星号为前缀)会捕获包括正斜线在内的整个URL部分,正斜线通常用于分隔路由参数。在上述示例中,该路由被映射到ErrorController的NotFound动作。

public class ErrorController: Controller
{
public ActionResult NotFound()
{
return new NotFoundResult();
}
}

现在,可以删去默认的{controller}/{action}/{id}路由,因为我们已经完全定制了路由,以匹配我们的URL模式。或者,你也许会选择保留它,以作为访问其他控制器的一种默认方式。

四、使用路由系统生成URL

每当网站中需要一个URL时,我们都要求框架给出,而不是采用硬编码。我们需要制定一种控制器、动作以及参数的组合,剩下的由ActionLink方法完成。ActionLink是MVC框架中HtmlHelper类上的一个扩展方法,它会生成一个插入了正确URL的完整的HTML<a>元素,该URL与传递进来的对象参数所指定的路由相匹配。以下是调用ActionLink的一个例子:

@Html.ActionLink ( "MVC3 in Action", "Show", "Catalog",
new { productCode = "mvc-in-action" }, null )

第一个是超链接的显示文本;第二个和第三个指定了要被链接到的动作和控制器;第四个采用了一个匿名类型形式的字典,以指定任意的附加路由参数;最后一个是仍以匿名类型形式指定的任意的附加HTML属性。

使用前面定义的路由,这个例子会生成一个链接,指向CatalogController上的Show动作,并带有为productCode指定的附加参数。以下是其输出:

<a href="/products/mvc-in-action">MVC3 in Action</a>

类似地,如果使用HtmlHelper的BeginForm方法来建立表单标签,它会为你生成URL。有时,能够将路由部分未指定的参数传递给动作时有用的:

@Html.ActionLink ( "MVC3 in Action", "Show", "Catalog",
new { productCode = "mvc-in-action", currency="USD" }, null )

如果该参数与路由中的某个部分匹配,它将成为URL的一部分。否则,它将被附加到查询字符串。比如,以下是上述代码生成的链接:

<a href="/products/mvc-in-action?currency=USD">MVC3 in Action</a>

在使用ActionLink时,被选中的路由是路由集合中所定义的第一个匹配路由。大多数情况下,这是足够的,但如果你希望请求一条特定的路由,可以使用RouteLink,它接受一个标识被请求路由的参数,像这样:

@Html.RouteLink ( "MVC3 in Action", "Show", "Catalog",
new { productCode = "mvc-in-action" }, null )

这个代码将查找一个带有product名称的路由,而不是指定的控制器和动作。

有时候你需要获得一个URL,但不是为了链接或表单。这通常发生在编写Ajax代码需要设置一个请求URL时。UrlHelper类能够直接生成URL,由ActionLink方法和其他方法所使用。以下是一个例子:

@Url.Action ( "Show", "Catalog",
new { productCode = "mvc-in-action" } )

这个代码也返回/products/mvc-in-action,但没有任何包围标签。

以路由控制URL的更多相关文章

  1. python 全栈开发,Day68(Django的路由控制)

    昨日内容回顾 1 MVC和MTV MTV 路由控制层(分发哪一个路径由哪一个视图函数处理) V : views (逻辑处理) T : templates (存放html文件) M : model (与 ...

  2. 5 解析器、url路由控制、分页、渲染器和版本

    1 数据解析器 1 什么是解析器 相当于request 中content-type 对方传什么类型的数据,我接受什么样的数据:怎样解析 无论前面传的是什么数据,都可以解开 例如:django不能解析j ...

  3. angularJs模块ui-router之路由控制

    在你的应用中大多数状态都有与其相关联的 url,路由控制不是设计完成 state 之后的事后想法,而是开始开发时就应该考虑的问题. 这里是如何设置一个基本url. $stateProvider .st ...

  4. Router和History (路由控制)-backbone

    Router和History (路由控制) Backbone.Router担任了一部分Controller(控制器)的工作,它一般运行在单页应用中,能将特定的URL或锚点规则绑定到一个指定的方法(后文 ...

  5. golang自定义路由控制实现(二)-流式注册接口以及支持RESTFUL

        先简单回顾一下在上一篇的文章中,上一篇我主要是结合了数组和Map完成路由映射,数组的大小为8,下标为0的代表Get方法,以此类推,而数组的值则是Map,键为URL,值则是我们编写对应的接口.但 ...

  6. rest_framework之解析器、路由控制、分页

    解析器 我们都知道,网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用 但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_fr ...

  7. Django-restframework之路由控制、解析器及响应器

    django-restframework之路由控制.解析器及响应器 一 前言 本篇博客介绍 restframework 框架的剩下几个组件,路由控制有三种:传统路由.半自动路由及全自动路由:解析器是用 ...

  8. web应用/路由控制/视图函数/单表多表操作

    一. 1.wen应用:BS架构的应用程序,B是浏览器,S:server(实现了wsgi协议)+ application https://www.cnblogs.com/liuqingzheng/art ...

  9. $Django patch与put,视图组件,路由控制,响应器

    1 patch与put(幂等?回顾) PATCH 与 PUT 属性上的一个重要区别还在于:PUT 是幂等的,而 PATCH 不是幂等的.幂等是一个数学和计算机学概念,在计算机范畴内表示一个操作执行任意 ...

随机推荐

  1. python自学笔记(九)python练习题

    1. 已知字符串 a = "aAsmr3idd4bgs7Dlsf9eAF",要求如下 1.1 请将a字符串的大写改为小写,小写改为大写 print a.swapcase() 1.2 ...

  2. WebAppScaner

    https://www.ohloh.net/p/simple-scan/ https://code.google.com/p/skipfish/ http://code.google.com/p/wa ...

  3. FastJson中@JSONField注解使用

    最近做项目中,使用了json格式在服务器之间进行数据传输.但是发现json格式数据不符合JAVA中的变量定义规则,并且难以理解,因此需要在后台中做二次处理,将数据处理成我们系统中定义的格式. 思路: ...

  4. 网页中获取网络mp3文件的时常

    <html> <audio id="audio" controls> <source src="http://cdn.kaishuhezi. ...

  5. cocos2d-x中的尺寸之一

    cocos2d-x中的尺寸函数繁多,官方文档对各种尺寸没有很好的描述,网上文章更加寥寥,对尺寸和位置的理解如果不到位,写代码就非常困难,需要猜测尝试,效率低下.这个文章我将研究一些我所迷惑的尺寸函数, ...

  6. C标准函数库中获取时间与日期、对时间与日期数据操作及格式化

    表示时间的三种数据类型[编辑] 日历时间(calendar time),是从一个标准时间点(epoch)到现在的时间经过的秒数,不包括插入闰秒对时间的调整.开始计时的标准时间点,各种编译器一般使用19 ...

  7. projecteuler----&gt;problem=8----Largest product in a series

    title: The four adjacent digits in the 1000-digit number that have the greatest product are 9 9 8 9 ...

  8. [置顶] 如何在Windows 7 64位安装Python,并使用Matplotlib绘图

    1.     安装Python 我使用的是Windows 7 64 bit,所以我从Python官网下载python-2.7.5.amd64.msi,安装步骤如下: 1)        安装windo ...

  9. SharePoint 2010 BCS - 简单实例(一)数据源加入

    博客地址 http://blog.csdn.net/foxdave 本篇基于SharePoint 2010 Foundation. 我的数据库中有一个病人信息表Patient,如今我就想把这个表中的数 ...

  10. CSS引入的方式有哪些? link和@import的区别是?转载

    CSS引入的方式有哪些? link和@import的区别是? HTML 中引入 CSS 的方式 有 4 种方式可以在 HTML 中引入 CSS.其中有 2 种方式是在 HTML 文件中直接添加 CSS ...