以下内容摘自:http://www.cnblogs.com/r01cn/archive/2011/11/16/2251693.html

感谢作者的翻译,这里只是译文。原书名:Pro ASP.NET MVC 3 Framework

 

第十一章 URL、路由、与区域 PART1

 

 

在引入MVC之前,ASP.NET假设在请求的URL与服务器硬盘上的文件之间有直接的关系。服务器的工作是接收浏览器的请求并递送相应文件的输出,如下所示:

Request URL
请求URL

Corresponding File
相应文件

http://mysite.com/default.aspx

e:\webroot\default.aspx

http://mysite.com/admin/login.aspx

e:\webroot\admin\login.aspx

http://mysite.com/articles/AnnualReview

File not found! Send error 404.
文件未找到!发送404错误

这种方式对Web表单工作得很好,在这里,每一个ASPX页面既是一个文件,又是一个对请求自包含的响应。这对MVC应用程序没什么意义,在这里,请求是由控制器类中的动作方法处理的,而且与硬盘上的文件没有一对一的相互关系。

为了处理MVC的网址,ASP.NET平台使用了路由系统。在本章中,我们将向你演示,如何建立和使用路由系统,以便为你的项目生成功能强大而灵活的URL处理。正如你将看到的,路由系统可以让你生成你所希望的任何URL模式,并且以清晰而简洁的方式表达他们。

路由系统介绍

路由系统有两个功能:

·         检查一个输入请求(1100.210应当是误输译者注),并想象出该请求想要的是哪个控制器和动作。正如你所期望的,在我们接收到一个客户端请求时,这就是我们希望路由系统去做的事情。

·         生成输出URL。这些是我们的视图渲染的HTML中出现的URL,以便用户点击这些连接时调用一个特定的动作(这时,它已经再次变成了输入URL)。

在本章的第一部分,我们将关注于定义路由,并使用它们去处理输入URL,以使用户能够到达你的控制器和动作。然后,我们将向你演示,如何使用那些同样的路由,来生成你需要包含在你的HTML中的输出URL

路由系统程序集

虽然路由系统是ASP.NETMVC框架所需要的,但也期望它被其它ASP.NET技术所使用,包括Web表单。由于这一原因,路由系统类在System.Web程序集中,而不是在System.Web.Mvc中。

当你生成一个新MVC应用程序时,你将看到Visual Studio已经添加了一个对System.Web.Routing程序集的引用。这是一个延期支持.NET 3.5的程序集(意即在.NET 4.0中仍用它对.NET 3.5进行支持,但对.NET 4.0的应用程序可以不用它译者注),而且对你的项目没有影响。如果你愿意,你可以删除这个引用。

我们关注于与MVC框架一起使用路由,但大量的信息,在与ASP.NET平台的其它部分使用路由时,也是适用的。Adam在他的《Applied ASP.NET 4 in Context》一书中包含了大量关于ASP.NET基础平台和Web表单使用路由的信息。

生成路由项目

为了演示路由系统,我们需要一个能够对之添加路由的项目。我们用Internet应用程序模板生成了一个新MVC应用程序,此项目名为UrlsAndRoutes。我们选择这个模板是因为它有一些现成的控制器和动作。

路由是在Global.asax中定义的。如果你在Visual Studio中打开这个文件,你将看到路由占据了这个文件的很大一部分(诚然,这还是短的)。清单11-1显示了我们项目的Global.asax,我们对它作了点编辑,以使它更可读。

注:严格地说,路由是在Global.asax.cs中定义的,这是Global.asax的后台代码文件(有些文章或书籍中也叫隐藏代码文件译者注)。当你在解决方案窗口中双击Global.asax时,Visual Studio实际上打开Global.asax.cs。出于这一原因,我们把这两个文件都称为Global.asax

Listing 11-1. The Default Global.asax.cs File

using System.Web.Mvc;
using System.Web.Routing;
namespace UrlsAndRoutes {
    publicclass MvcApplication : System.Web.HttpApplication {
        protectedvoid Application_Start() {
            AreaRegistration.RegisterAllAreas();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }
        public static void RegisterRoutes(RouteCollection routes) {
            ... routes are defined here...
        }

        publicstatic void RegisterGlobalFilters(GlobalFilterCollection filters) {
            filters.Add(new HandleErrorAttribute());
        }
    }
}

Application_Start方法是由当前ASP.NET平台在应用程序启动时首先调用的,它导致RegisterRoutes方法被调用。送给这个方法的参数是静态的RouteTable.Routes属性的值,这是RouteCollection类的一个实例。

我们已经从RegisterRoutes方法中删除了被默认添加的路由,因为我们想给你演示生成路由的各种技术,以及可用路由的不同种类。在我们能够做这些事情之前,我们需要退一步,先考察一些路由系统核心的东西:URL模式。

介绍URL模式

路由系统用一组路由来实现它的魔法。这些路由共同组成了应用程序的URL模式或方案,这个URL模式(或方案)是你的应用程序能够认识并能对之作出响应的一组URL

我们不需要手工键入我们打算支持的所有个别的URL。而是,每条路由都含有一个URL模式,用它与一个输入URL进行比较。如果该模式与这个URL匹配,那么它(URL模式)便被路由系统用来对这个URL进行处理。

注:上两段的含义可以这样理解:路由系统由若干条路由所组成;每条路由都有一个URL模式;一个URL模式相当于表示URL的一个公式,有若干URL与这个模式相匹配;应用程序中要定义的就是与URL模式相关的路由;对一个请求进行服务时,路由系统查看这个URL与哪个URL模式相匹配,便用这个模式对应的路由对这个URL进行处理。有点绕口,但仔细阅读应该能理解。译者注

让我们从SportsStore应用程序的URL例子开始:

http://mysite.com/Admin/Index

这是我们用来访问产品目录的管理员视图的URL。如果你回过头去参考第9章,你会看到这个URL指向了AdminController类中的Index动作方法。

URL可以被分解成片段。这些片段是这个URL除主机名和查询字串以外的、以/字符分隔的各个部分。在这个URL示例中,有两个片段,如图11-1所示。

ASP.NET MVC URL、路由、与区域 Part1

图11-1. 示例URL的片段 

第一片段含有单词Admin,而第二片段含有单词Index。很显然,第一个片段与控制器有关,第二片段与动作有关。但是,当然地,我们需要以一种路由系统可以理解的方式来表示这种关系。以下是做这件事的一个URL模式:

{controller}/{action}

当处理一个输入URL时,路由系统的工作是把这个URL与一个模式匹配,然后针对这个模式中定义的片段变量,从这个URL中提取相应的值。片段变量用花括号({}字符)表示。这个示例模式有名字为controlleraction的两个片段变量。

我们说,与一个模式匹配,因为一个MVC应用程序通常会有几个路由,而路由系统会把输入URL与每个路由的URL模式相比较,直到找到一条匹配的。

注:路由系统没有任何控制器和动作的专门知识。它只是为片段变量提取值,并沿请求管道传递它们(注意,管道中传递的是 —译者注)。在管道之后,当请求恰好到达MVC框架之时,其含意(指)被赋给controlleraction变量。这是为什么路由系统可以被用于Web表单、以及我们能够如何生成我们自己的变量的原因。

默认地,一个URL模式将匹配有正确片段数的任何URL。例如,上例的模式将匹配任何有两个片段的URL,如表11-1所示。

11-1. 匹配URL

请求URL

片段变量

http://mysite.com/Admin/Index

controller = Admin action = Index

http://mysite.com/Index/Admin

controller = Index
action = Admin

http://mysite.com/Apples/Oranges

controller = Apples
action = Oranges

http://mysite.com/Admin

No match—too few segments
不匹配片段太少

http://mysite.com/Admin/Index/Soccer

No match—too many segments
不匹配片段太多

11-1高亮了URL模式的两个关键行为:

·         URL模式是保守的,而且只匹配与模式具有相同片段数的URL。你可以在表中的第四、第五个例子看到这种情况。

·         URL模式是宽松的。如果一个URL正好具有正确的片段数,该模式就会提取片段变量的值,而不管它可能是什么。

这些是默认行为,这是理解URL模式如何运行的关键。你将在本章稍后看到如何修改这种默认行为。

正如我们提到过的,路由系统并不知道关于MVC应用程序的任何情况,因此,即使在没有控制器或动作与从一个URL提取出来的值相符时,路由系统也会进行匹配。你可以通过表11-1的第二个例子明白这种情况的演示。我们在URL中传递了AdminIndex片段,因此,从这个URL提取的值也已经被传递(路由系统并不管是否有相应的控制器和动作方法译者注)。

生成并注册一个简单的路由

一旦你在头脑中已经有了一个URL模式,你就可以用它来定义一个路由。清单11-2演示了,如何用上一小节的URL模式示例,在Global.asaxRegisterRoutes方法中生成一条路由。

Listing 11-2. Registeringa Route

public static void RegisterRoutes(RouteCollection routes) {
    Route myRoute = new Route("{controller}/{action}"new MvcRouteHandler());
    routes.Add("MyRoute", myRoute);

}

我们生成了一个新的路由对象,以我们的URL模式作为构造器参数。我们也传递了MvcRouteHandler的一个实例。不同的ASP.NET技术提供了不同的类来设计路由的行为,而这个(指MvcRouteHandler译者注)是我们用于ASP.NET MVC应用程序的类。一旦我们已经生成了这个路由,我们就可以用Add方法把它添加到RouteCollection对象,在其中传递我们给这个路由所起的名字,和我们已经生成的这个路由。

提示:命名你的路由是可选的,而且有一种争论,这样做牺牲了路由其它方面的一些关注分离(这句可能没译好译者注)。我们对命名是相当宽松的,但我们会在本章稍后的从指定路由生成一个URL”小节中,解释为什么这可能是一个问题。

注册路由的一个更方便的办法,是使用在RouteCollection类中定义的MapRoute方法。清单11-3演示了我们如何才能用这个方法来注册我们的路由。

Listing 11-3. Registering a Route Using the MapRoute Method

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("MyRoute","{controller}/{action}");
}

这个办法要更紧凑一点,主要是因为我们不需要生成MvcRouteHandler的一个实例。MapRoute方法专用于MVC应用程序。ASP.NETWeb表单应用程序可以用MapPageRoute方法,它也是在RouteConllection类中定义的。

单元测试:测试输入路由

即使你对应用程序的其余部门不采用单元测试,我们也建议你单元测试路由,以确保它们按预期的来处理输入URL。在大型应用程序中,URL方案可能会相当复杂,很容易生成一些意外的结果。

在上一章中,为了保持单元测试描述是自包含的,我们已经避免生成在测试之间共享的通用辅助方法。对于本章,我们要采用不同的办法。测试一个应用程序的路由方案是最容易完成的,因为你可以在一个单一的方法中批处理几个测试。并且,利用某些辅助方法,这会变得容易很多。

为了测试路由,我们需要模仿三个类:HttpRequestBaseHttpContextBase、和HttpResponseBase(最后一个是测试输出URL所需要的,我们会在本章稍后涉及它)。合在一起,这些类重新生成了足以支持路由系统的MVC架构。

以下是生成这些模仿对象的辅助方法,我们把它加到了我们的测试项目:

private HttpContextBase CreateHttpContext(stringtargetUrl = null,
string httpMethod ="GET") {
    // create the mock request
    Mock<HttpRequestBase> mockRequest = newMock<HttpRequestBase>();
    mockRequest.Setup(m =>m.AppRelativeCurrentExecutionFilePath).Returns(targetUrl);
    mockRequest.Setup(m =>m.HttpMethod).Returns(httpMethod);
    // create the mock response
    Mock<HttpResponseBase> mockResponse =new Mock<HttpResponseBase>();
    mockResponse.Setup(m =>m.ApplyAppPathModifier(
    It.IsAny<string>())).Returns<string>(s=> s);
    // create the mock context, using the requestand response
    Mock<HttpContextBase> mockContext = newMock<HttpContextBase>();
    mockContext.Setup(m =>m.Request).Returns(mockRequest.Object);
    mockContext.Setup(m =>m.Response).Returns(mockResponse.Object);
    // return the mocked context
    return mockContext.Object;
}

这里的setup相对简单。我们通过HttpRequestBase类的AppRelativeCurrentExecutionFilePath属性暴露了我们想要测试的URL,并且通过模仿HttpContextBase类的Request属性暴露了HttpRequestBase。我们的下一个方法让我们测试一个路由:

private void TestRouteMatch(string url, string controller, string action, object
routeProperties = nullstring httpMethod ="GET") {
    //Arrange
    RouteCollectionroutes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    // Act -process the route
    RouteDataresult = routes.GetRouteData(CreateHttpContext(url, httpMethod));
    // Assert
    Assert.IsNotNull(result);
    Assert.IsTrue(TestIncomingRouteResult(result,controller, action, routeProperties));
}

该方法的参数让我们指定要测试的URLcontrolleraction片段变量所期望的值,以及一个对象,它含有为我们已经定义的附加变量所期望的值。我们在本章稍后将给你演示如何生成这种变量。我们也为HTTP方法定义了一个参数,我们将在强制路由小节解释它。

TestRouteMatch方法依赖于另一个方法,TestIncomingRouteResult,以比较从路由系统获得的结果和我们期望的分段变量的值。这个方法使用了.NET反射,以使我们能够使用一个匿名类型来表示任何附加的分段变量。如果对这个方法不理解,不用着急,因为其作用只是让测试更方便:它不是理解MVC所需要的。

以下是这个TestIncomingRouteResult方法:

private bool TestIncomingRouteResult(RouteData routeResult, string controller,
string action, object propertySet = null) {
    Func<objectobjectbool> valCompare =(v1, v2) => {
        returnStringComparer.InvariantCultureIgnoreCase.Compare(v1, v2) == 0;
    };
    bool result =valCompare(routeResult.Values["controller"], controller)
        &&valCompare(routeResult.Values["action"], action);
    if (propertySet != null) {
        PropertyInfo[] propInfo =propertySet.GetType().GetProperties();
        foreach (PropertyInfo pi in propInfo) {
            if(!(routeResult.Values.ContainsKey(pi.Name)
                    &&valCompare(routeResult.Values[pi.Name],
                    pi.GetValue(propertySet, null)))) {
                result = false;
                break;
            }
        }
    }
    return result;
}

我们也需要一个方法来检查一个URL不工作。正如你将看到的,这可能是定义一个URL方案的重要部分。

private void TestRouteFail(string url) {
    // Arrange
    RouteCollection routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    // Act - process the route
    RouteData result = routes.GetRouteData(CreateHttpContext(url));
    //Assert
    Assert.IsTrue(result ==null || result.Route == null);
}

TestRouteMatchTestRouteFail包含了对Assert方法的调用,如果断言失败,它会弹出一个异常。因为C#异常被上传到调用堆栈,我们可以生成简单的测试方法,它能够测试一组URL,并得到我们需要的测试行为。以下是一个我们用来测试清单11-3中路由的测试方法:

[TestMethod]
public void TestIncomingRoutes() {
    // checkfor the URL that we hope to receive
    TestRouteMatch("~/Admin/Index","Admin""Index");
    // checkthat the values are being obtained from the segments
    TestRouteMatch("~/One/Two","One""Two");
    //ensure that too many or too few segments fails to match
    TestRouteFail("~/Admin/Index/Segment");
    TestRouteFail("~/Admin");
}

这个测试用TestRouteMatch方法来检查我们期望的URL。而且也检查一个同样格式的URL,以确保用URL分段恰当地获得controlleraction的值。我们也用TestRouteFail方法来确保我们的应用程序不接收有不同分段数目的URL。当进行测试时,我们必须用波浪线(~)字符作为URL的前缀,因为这是ASP.NET框架如何把URL表现给路由系统的。

注意,我们不需要在测试方法中定义路由。这是因为我们从Global.asax类中的RegisterRoutes方法中直接装载它们。

你可以通过启动应用程序看到我们已经定义的路由的结果。当浏览器请求默认URLhttp://localhost:<端口>),应用程序将返回一个“404 — 未找到响应。这是因为我们还没有生成用于这个URL的路由,只支持{controller}/{action}格式。为了测试这种URL,导航到~/Home/Index。你可以看到应用程序生成了如图11-2所示的结果。

ASP.NET MVC URL、路由、与区域 Part1

图11-2. 手工测试URL模式

 

我们的URL模式已经处理了这个URL,并为controller变量提取了Home值,为action变量提取了Index值。MVC框架把这个请求映射到了Home控制器的Index方法,这是在我们选择Internet应用程序的MVC项目模板时,VS为我们已经生成的。

至此,你已经生成了你的第一个路由,并用它来处理一个输入URL。在以下小节中,我们给你演示如何生成更复杂的路由,给出用于MVC应用程序的更丰富和更灵活的URL方案。

定义默认值

当我们请求应用程序的默认URL时,出现错误的原因是它不匹配我们已经定义的路由。默认URL被表示成~/送给路由系统,因此,没有与controlleraction变量匹配的片段。

前面我们曾解释过,URL模式是保守的,它们只匹配指定片段数的URL。我们也说过,这是默认行为。修改这种行为的一个办法是使用默认值。当URL不含与一个值匹配的片段时,用默认值。清单11-4提供了一个含有默认值的路由的例子。

Listing 11-4. Providing aDefault Value in a Route

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("MyRoute","{controller}/{action}"new { action = "Index" });
}

默认值是作为匿名类型的属性来提供的。在清单11-4中,我们已经为action变量提供了一个Index的默认值。这个路由将匹配所有两片段的URL,就像它之前所做的那样。例如,如果请求http://mydomain.com/Home/Index,该路由将为controller提取Home值,为action提取Index值。

现在,我们已经为action片段提供了一个默认值,该路由也将匹配单片段URL匹配。当处理URL时,路由系统将从这个单片段URL提取controller值,并为action变量这个默认值。这样,我们可以请求http://mydomain.com/Home,并调用Home控制器上的Index动作方法。

我们可以继续向前,并定义根本不含任何片段变量的URL,只依靠默认值来标识controlleraction。我们可以用默认值为这两者映射默认URL,如清单11-5所示。

Listing 11-5. Providing Action and Controller Default Values in a Route

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("MyRoute","{controller}/{action}",
    new { controller ="Home", action = "Index" });
}

通过为controlleraction变量提供默认值,我们已经生成了与零个、一个、或两个片段的ULR匹配的路由,如表11-2所示。

11-2 Matching URLs

Number of Segment

Example

Maps To

0

mydomain.com

controller = Home
action = Index

1

mydomain.com/Customer

controller = Customer
action = Index

2

mydomain.com/Customer/List

controller = Customer
action = List

3

mydomain.com/Customer/List/All

No match—too many segments

我们在输入URL中接收的片段越少,我们依靠的默认值越多。如果我们再次运行此应用程序,浏览器将再次请求默认URL,但这次我们的新路由将起作用了,并为controlleraction添加默认值,允许这个输入URL被映射到Home控制器中的Index动作,如图11-3所示。

ASP.NET MVC URL、路由、与区域 Part1

图11-3. 添加一条用于默认URL的路由 

单元测试:默认值

如果我们用我们的辅助方法来测试定义默认值的路由,我们不需要采取任何特殊的动作。例如,以下是对清单11-5路由的简单测试:

[TestMethod]
public void TestIncomingRoutes() {
    TestRouteMatch("~/","Home""Index");
    TestRouteMatch("~/Customer","Customer""Index");
    TestRouteMatch("~/Customer/List","Customer""List");
    TestRouteFail("~/Customer/List/All");
}

需要说明的一点是,我们必须把默认URL指定为~/,因为这是ASP.NET如何把URL表示给路由系统的。如果我们用空字符串(””)或/来定义路由,路由系统将弹出一个异常,而测试将会失败。

使用静态URL片段

并不是一个URL模式中的所有片段都需要是可变的。你也可以生成具有静态片段的模式。假如我们想匹配像这样的URL,以支持带有前缀PublicURL

http://mydomain.com/Public/Home/Index

我们可以通过使用像清单11-6所示的模式来做这件事。

Listing 11-6. A URL Pattern with Static Segments

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("MyRoute","{controller}/{action}",
    new { controller = "Home", action ="Index" });
   routes.MapRoute("","Public/{controller}/{action}",
   new {controller = "Home", action = "Index" });

}

这个URL模式将只匹配含有三个片段的URL,第一个必须是Public(原文为Customers,不对译者注)。其它两个片段可以含有任何值,并将被用于controlleraction变量。

我们也可以生成既有静态也有可变元素的片段的URL模式,比如,清单11-7所表示的这种。

Listing 11-7. A URL Pattern with a Mixed Segment

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("","X{controller}/{action}");
    routes.MapRoute("MyRoute","{controller}/{action}",
        new{ controller = "Home", action = "Index" });
    routes.MapRoute("","Public/{controller}/{action}",
        new { controller = "Home", action= "Index" });
}

这条路由中的模式匹配任意两片段URL,而第一个片段以字母X打头。用于controller的值取自第一个片段,除X以外。action的值取自第二个片段。作为一个例子,以下URL与这条路由匹配:

http://mydomain.com/XHome/Index

这个URL被指向Home控制器上的Index动作方法。

路由排序

在清单11-6中,我们定义了一条新路由,并把它放在RegisterRoutes方法中的所有其它路由之前。我们这么做,是因为路由是以它们在RouteCollection对象中出现的顺序被运用的。MapRoute方法把一条路由添加到该集合的末端,意即,路由一般地是按我们添加他们的顺序被运用的。我们说一般地,是因为有办法让我们把路由插入到指定位置。我们不倾向于使用这种办法,因为让路由以它们被定义的顺序来运用,更容易理解用于一个应用程序的路由。

路由系统试图根据首先被定义的路由模式来匹配一个输入URL,并且只在不匹配时才会处理下一条路由。路由依次被试,直到找到匹配的一条,或这组路由被试完。其结果是,我们必须首先定义比较特殊的路由。我们在清单11-7中添加的路由要比以下路由更特殊。假设我们颠倒路由的顺序,像这样:

routes.MapRoute("MyRoute","{controller}/{action}",
    new{ controller = "Home", action = "Index" });
    routes.MapRoute("","X{controller}/{action}");

那么,第一条路由,它匹配任何具有零、一、二片段的URL,将是被使用的一条。更特殊的路由,现在是该清单的第二条,将是不可到达的。这条新路由去除一条URL的前导X,但旧路由(指第一条 译者注)却做不到,因此,像这样的一条URL

http://mydomain.com/XHome/Index

将以名为XHome的控制器为目标,而这是不存在的,因此,将导致一个“404 — 未找到错误被发送给用户。

如果你还没有阅读单元测试输入URL这一小节,我们建议你现在就读。如果你只对你应用程序的一个部分做单元测试,这应该就是你的URL方案。

我们可以结合静态片段和默认值为特定的路由生成一个别名。如果你已经公开地发布了你的URL方案,并且它与你的用户形成了标准合同,那末,这可能是用有的。如果你在这种情况下重构你的应用程序,你需要保留以前的URL格式。让我们设想,我们以前用一个Shop控制器,现在要由Home控制器来替代。清单11-8演示了我们如何才能生成一个保留旧式URL方案的路由。

Listing 11- 8. Mixing Static URL Segments and Default Values

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("ShopSchema","Shop/{action}"new { controller = "Home" });
    ...otherroutes...
}

我们添加的路由匹配任何两片段的URL,第一个片段是Shopaction的值取自第二个URL片段。这个URL模式不含对控制器的可变片段,因而使用我们所提供的默认值。这意味着对Shop控制器上一个动作的请求被转换成对Home控制器的请求。而且,我们可以更进一步,生成动作方法的别名,它也被重构,且不再出现在控制器中。为此,我们简单地生成一个静态URL,并给controlleraction提供默认值,如清单11-9所示。

Listing 11-9. Aliasing a Controller and an Action

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("ShopSchema2","Shop/OldAction",
        new {controller = "Home", action = "Index" });

    routes.MapRoute("ShopSchema""Shop/{action}"new {controller = "Home" });
    ...other routes...
}

再一次注意,我们放置新路由的位置,以使它先被定义。这是因为它比后面的路由更特殊。例如,如果一个对Shop/OlaAction的请求被下一条路由定义来处理,我们就会得到与我们想要的不同的结果。这个请求将被处理成一个“404 — 未找到错误,而不是被转换以保持与我们客户的标准合同。

单元测试:测试静态片段

再一次地,我们可以把我们的辅助方法用于其URL模式含有静态片段的路由。

以下是测试清单11-8中所添加的路由的例子:

[TestMethod]
public void TestIncomingRoutes() {
    TestRouteMatch("~/Shop/Index","Home""Index");
}

定义自定义片段变量

我们并不受限于controlleraction变量。我们也可以定义自己的变量,如清单11-10所示。

Listing 11-10. Defining Additional Variables in a URL Pattern

public static void RegisterRoutes(RouteCollection routes) {
    routes.MapRoute("MyRoute","{controller}/{action}/{id}",
        new { controller = "Home", action ="Index"id ="DefaultId" });
}

该路由的URL模式定义了典型的controlleraction变量,以及一个名为id的自定义变量。这条路由将匹配任何0-3个片段的URL。第三个片段的内容将被赋给id变量,而如果没有第三个片段,将采用默认值。

注意:有些名字是保留的,因而对自定义片段变量名是非法的。这些是controlleraction、和area。前两个的含义是显然的,我们将在本章稍后的与区域一起工作小节中解释区域(area)的作用。

我们可以通过使用RouteData.Values属性,在一个动作方法中访问任何一个片段变量。为了对此演示,我们把一个叫做CustomVariable的动作方法添加到HomeController类,如清单11-11所示。

Listing 11-11. Accessing a Custom Segment Variable in an Action Method

public ViewResult CustomVariable() {
    ViewBag.CustomVariable= RouteData.Values["id"];
    returnView();
}

这个方法获取路由的URL模式中的自定义变量的值,并用ViewBag把它传递给视图。清单11-12显示了该方法的相应视图,CustomVariable.cshtml,我们把它放在了/Views/Home文件夹中。

Listing 11-12. Displaying the Value of a Custom Segment Variable

@{
    ViewBag.Title = "CustomVariable";
}
<h2>Variable:@ViewBag.CustomVariable</h2>

如果你运行应用程序,并导航到/Home/CustomVariable/Hello网址,Home控制器中的CustomVaiable动作方法就会被调用,自定义片段变量的值就会通过ViewBag被接收并被显示出来,如图产11-4所示。

ASP.NET MVC URL、路由、与区域 Part1

图11-4. 显示自定义片段变量的值

 

单元测试:测试自定义片段变量

在我们的测试辅助方法中,我们包括了对测试自定义片段变量的支持。TestRouteMatch方法有一个可选的参数,它接受一个匿名类型,该匿名类型含有我们想要测试的属性名和我们期望的值。以下是一个测试方法,它测试在清单11-10中定义的路由:

[TestMethod]
public void TestIncomingRoutes() {
    TestRouteMatch("~/","Home""Index"new {id = "DefaultId"});
    TestRouteMatch("~/Customer","Customer""index"new { id = "DefaultId" });
    TestRouteMatch("~/Customer/List","Customer""List"new { id = "DefaultId" });
    TestRouteMatch("~/Customer/List/All","Customer""List"new { id = "All" });
    TestRouteFail("~/Customer/List/All/Delete");
}

 

用自定义变量作为动作方法参数

使用RouteData.Values属性只是访问自定义路由变量的一种办法。另一种办法要优雅得多。如果我们用匹配URL模式的变?‡

相关文章:

  • 2021-12-25
  • 2022-12-23
  • 2021-08-15
  • 2022-12-23
  • 2021-09-10
  • 2022-02-06
  • 2021-09-28
猜你喜欢
  • 2022-02-23
  • 2021-12-06
  • 2021-10-03
  • 2021-10-24
  • 2021-07-25
相关资源
相似解决方案