ASP.NET Routing 101

介绍

 

以前的做法

先前的ASP.NET Web Form开发时期,一个请求总是对应一个特定的物理文件,在文件的路径后用?key=value;的方式来传递参数,http://www.msn.com/books/products.aspx?name=twilight,这种方式不便于用户去记忆,也对SEO不够友好。相对于http://www.msn.com/books/twilight来说后者更具有可读,可记忆性。

还可以使用 URL 模式通过编程方式来创建对应于路由的 URL。这使您能够集中逻辑用于创建 ASP.NET 应用程序中的超链接。

 

对比 URL Rewriter

早期开源的URL Rewriter开始利用正则表达式匹配,将一个匹配规则请求进行转换并跳转另一个对应物理文件的URL上,从而实现上面所提到例子的功能。ASP.NET Routing则是直接将请求发送到对应的文件或者对应类和其方法的,从这一点来说效率是优于URL Rewriter的。

 

在MVC中的应用

由于MVC中使用Routing作为默认的路由工具,致使大多数人都认为其是MVC的一部分,这里澄清一下,MVC只是依赖Routing去做URL路由,将一个请求导向到一个Controller里的Action并生成其余的参数,而Routing本身还可以将请求导向到具体的文件,用作于Web Form开发。

接下来我们以MVC为例来讲解怎么进行Routing的配置,使用和测试。

 

MVC中的Routing

 

打开VS,新建一个项目之后,在Global.asax.cs中我们能看到有默认的Routing规则:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace BlueMvc
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterRoutes(RouteTable.Routes);
        }
    }
}

分析上面代码可以看到,在应用启动时的Application_Start方法中去注册Router,所有的规则都是存放在RouteCollection这个集合的实例下面,可以通过该对象的Add方法进行追加新的Routing规则。

MVC中RouteCollectionExtensions类对ASP.NET Routing进行的扩展,新增了MapRoute方法用于对Controller和Action进行的映射,上面代码就是使用了MapRoute来进行添加路由规则的。我们再来看下MapRoute这个方法的定义。

static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);

参数具体的祥解

string routeName, 该路由规则的名称

string url, 约定要进行匹配URL的规则,如上面代码中的"{controller}/{action}/{id}", 只能匹配“product/search/101”这样三段式的URL

object defaults, 该参数指名要传递的Controller和Action

object constraints, 该参数用来对url中的规则进行约束,例如,指定id只能为数值型,Action名字等等。

 

定义URl Route

 

路由定义 匹配 URL 示例

{controller}/{action}/{id}

/Products/show/beverages

blog/{action}/{entry}

/blog/show/123

/report/{type}/{year}/{month}/{day}

/report/sales/2008/1/5

{language}-{country}/{action}

/zh-cn/show

{controller}/{action}/{*id}

/Products/show/1/0/1

 

占位符

在路由中,您可以通过用大括号{ 和 }来定义占位符(称为“URL 参数”)。在分析 URL 时将 / 字符解释为分隔符。将路由定义中将不是分隔符和不在大括号中的信息视为一个常量值。(URL中只有三种值,占位符,/分隔符)、常量)

您可以在分隔符之间定义多个占位符,但必须用一个常量值分隔开。例如,{language}-{country}/{action} 是有效的路由模式。但是,由于占位符之间没有常量或分隔符,所以 {language}{country}/{action} 不是有效的模式。

 

*可变数量段

有时您需要处理包含可变数量的 URL 段的 URL 请求。定义路由时,通过将参数标记星号 (*) 可以指定最后一个参数应与 URL 的其余部分匹配。因而该参数称为“全部捕捉”参数。

{controller}/{action}/{*id}

 

默认值

如果 URL 没有包括该参数的值,则会使用默认值。通过将字典分配给 Route 类的 Defaults 属性,可以设置路由的默认值,上面代码中也有使用默认值,如下代码:

new { controller = "Home", action = "Index", id = UrlParameter.Optional }

 

约束

约束是通过使用正则表达式或使用实现 IRouteConstraint 接口的对象来定义的。将路由定义添加到 Routes 集合时,同时也通过创建一个包含验证测试的 RouteValueDictionary 对象添加了约束。然后将此对象分配给 Constraints 属性。字典中的关键字标识约束适用的参数。字典中的值可以是表示正则表达式的字符串,也可以是实现 IRouteConstraint 接口的对象。

提供字符串后,路由将视字符串为正则表达式,并通过调用 Regex 类的 IsMatch 方法检查参数值是否有效。总是将正则表达式视为不区分大小写。

new { controller = @"^\w+", action = @"^\w+", id = @"^\d+", httpMethod = “POST” });

 

顺序

Request进来之后按照Route的顺序来进行匹配,一量URL满足最先能匹配的规则时,则URL Routing会对该URL进行处理,提取URL信息,生成一个RouteData,使用实现IRouteHandler接口的Hanlder来执行具体映射,并且不会再去匹配处理后面的规则,所以在设置Routes顺序时有以下两条准则。

  • 将子集放到前面,父集放在后面。
  • 将父集加上更祥细的约束

RoutingExistFiles,该属性指定是否要在指不到对应的规则情况下去寻找该URL对应位置的物理文件。

 

重要的命名空间、类和接口

 

  • System.Web.Routing - 此命名空间提供用于URL路由的类 
  • IRouteHandler - 路由处理程序的接口,自定义的路由处理程序都要实现这个接口 
  • RequestContext - 封装所请求的路由的相关信息和当前的http上下文 
  • RouteData - 所请求路由相关信息,如Controller和Action的值 
  • RouteCollection - 路由集合 
  • RouteValueDictionary - 字典表,用来包含各样信息,不区分大小写 
  • Route - 继承RouteBase,内含路由相关信息

 

最佳实践

 

很多人担心如果前期URL设置不好或有新的Features加入如何修改Routing配置的问题,就目前应用结果来说这点担心有点多余,URL请求规则是在产品发布时定义好的,在产品修改时才会作出修改,换句话说配置改变的几率很小。

当然,我们仍可以将URL Routing放在外部配置文件中。具体作法这里先不谈。

 

单元测试

 

在测试中使用Moq Mock框架,模拟一次URL请求,通过判断route执行结果返回的Controller和Action值来确认Routing的正确性。

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Web;
using System.Web.Routing;
using Core;

namespace Tests
{
    [TestClass]
    public class RoutingTest
    {
        [TestMethod]
        public void TestRouting()
        {
            const string url = "~/";

            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.Setup(p => p.AppRelativeCurrentExecutionFilePath).Returns(url);
            mockRequest.Setup(p => p.PathInfo).Returns(string.Empty);
            HttpRequestBase request = mockRequest.Object;

            var mockContext = new Mock<HttpContextBase>();
            mockContext.Setup(p => p.Request).Returns(request);
            HttpContextBase context = mockContext.Object;

            RouteTable.Routes.Clear();

            BuleLiteApplication.RegisterRoutes(RouteTable.Routes);
            var routeData = RouteTable.Routes.GetRouteData(context);

            Assert.AreEqual("Home", routeData.Values["controller"]);
            Assert.AreEqual("Index", routeData.Values["action"]);
        }
    }
}
posted @ 2010-04-24 22:54  Zhiming Jiang  阅读(539)  评论(0编辑  收藏  举报