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

自定义路由系统

通过前面的学习,我们能够感受到路由系统的灵活性和可配置性,如果这些不能满足我们的需求,我们可以自定义行为,下面会介绍如何自定义路由。

创建一个对RouteBase类的自定义实现

如果我们不喜欢标准Route对象匹配URL的方式,或者想实现一些特别的东西。我们可以从RouteBase派生一个类来替代。派生出来的类可以让我们自己控制URL的匹配,参数的提取,以及Outgoing URL的创建。派生的类需要实现下面两个方法:

1.GetRouteData(HttpContextBase httpContext):实现传入的URL的匹配机制。
   在每一个RouteTable.Routes进入的时候,MVC框架会轮流调用该方法直到其中的一次调用返回非空值。

2.GetVirtualPath(RequestContext requestContext, RouteValueDictionary values):实现创建传出的URL机制。
   在每一个RouteTable.Routes进入的时候,MVC框架会轮流调用该方法直到其中的一次调用返回非空值。

下面用一个例子来说明,假设现在我们要迁移一个程序到MVC,但是一些用户已经存了程序链接书签或者在脚本里面硬编码了。我们仍然需要支持原来的URL,我们能够通过常规的路由系统来处理这种情况,这里用这个例子来说明自定义路由。

创建一个LegacyController,代码如下:

View Code

        public ActionResult GetLegacyURL(string legacyURL)
{
return View((object)legacyURL);
}

在这个简单的Controller里面,GetLegacyURL action方法会获取一个参数并将这个参数传递给View。如果我们真的的要实现这个Controller,需要使用这个方法来检索请求的文件,但是这里为了简便,只是在View里面显示请求的URL。注意:我们这里对View里面参数进行了Object的类型转换。原因是View()方法本身有一个string参数的重载,如果这里我们不转换,View会把string参数当成某一个视图的名字去寻找,显然这里我们不是要跳转到某一个View,而是在默认的View上显示出来就行。如图所示:

转成object后,会认为是model而使用默认的View呈现。接着添加视图GetLegacyURL.cshtml,代码如下:

View Code

@model string
@{
ViewBag.Title = "GetLegacyURL";
}

GetLegacyURL


The URL requested was:@Model

上面我们已经实现了从RouteBase派生,下面接着创建一个类LegacyRoute,如下所示:

View Code

namespace UrlsAndRoutes.Infrastructure
{
public class LegacyRoute : RouteBase
{
private string[] urls;
public LegacyRoute(params string[] targetUrls)
{
urls = targetUrls;
}


public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Legacy");
result.Values.Add("action", "GetlegacyURL");
result.Values.Add("legacyURL", requestedURL);
}
return result;
}

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData result = null;
if (values.ContainsKey("legacyURL") && urls.Contains((string)values["legacyURL"], StringComparer.OrdinalIgnoreCase))
{
result = new VirtualPathData(this, new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1));
}
return result;
}
}
}

该类的构造器需要传入一个字符串数组作为参数,该参数呈现这个路由类支持的个别的URL。在GetRouteData方法里面定义的部分就是路由系统调用来检查是否能够处理传入的传入的URL。如果不能处理请求,返回null,并且路由系统会移动到到下一个路由重复该过程。如果能够处理请求,就需要返回包含了controller和action变量以及任何其他的想要传递给action方法的RouteData类的实例。

当我们创建RouteData对象时需要传入要处理的值的handler,这里我使用的是MvcRouteHandler类如:result=new RouteData(this,new MvcRouteHandler());
对绝大多数MVC应用程序,这个就是我们需要的类,因为它联接着路由系统与一个MVC程序的controller/action model。当然,我们也可以实现一个替代MvcRouteHandler的类,也就是后面会介绍的创建自定义的Route Handler.

在上面的routing实现里,我们将会路由任何传给构造器的请求的URL。当获取这样一个URL时,我们添加controller和action的硬编码值到RouteValues对象,我们也会传递请求的URL作为legacyURL属性,注意这里的属性的名字必须和action方法的参数名匹配,从而保证了我们创建的值能够通过参数传递给action方法。最后一步就是注册从RouteBase派生的新的路由。如:routes.Add(new LegacyRoute(“~/article/Windows_3.1_Overview.html”,”~/old/.NET_1.0_Class_Library”));我们创建了一个LegacyRoute类的实例,并传入了我们要路由的URL,然后使用Add方法添加到RouteCollection里面。当我们请求传入的URL时,就会被路由到我们定义的Controller,如下图所示:

创建Outgoing URL

为了支持创建Outgoing URL,我们需要实现GetVirtualPath方法。再一次说明,如果我们不能处理某个请求,我们通过返回null值来让路由系统知道,否则就返回VirtualPathData类的实例。

代码如下:

View Code

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData result = null;
if (values.ContainsKey("legacyURL") && urls.Contains((string)values["legacyURL"], StringComparer.OrdinalIgnoreCase))
{
result = new VirtualPathData(this, new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1));
}
return result;
}

我们在传递segment变量或其他详细的值时通常使用了匿名类型,但是在这里,路由系统将这些转换成了RouteValueDictionary对象。所以我们像这样添加一个链接在View里面,

如下所示:

View Code

@Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL =  
"~/articles/Windows_3.1_Overview.html" })

创建的具有legacyURL属性的匿名类型被转换成了RouteValueDictionary对象,并且包含了一个同名的key。

在本例里面,如果有一个key名为legacyURL并且它的值是我们传入LegacyRoute类的构造器里面的URL中的一个,我们决定能处理传出的URL请求。

创建自定义路由处理程序(Route Handler)

前面我们提到过,在我们的路由里面会依赖MvcRouteHandler类,因为它联系着路由系统跟MVC框架。我们可以通过实现IRouteHandler接口来自定义Route Handle1r,如下所示:

View Code

namespace UrlsAndRoutes.Infrastructure
{
public class CustomRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CustomHttpHandler();
}
}

public class CustomHttpHandler : IHttpHandler
{

public bool IsReusable
{
get { return false; }
}

public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello");
}
}
}

IRouteHandler接口的目的就是提供一种方式来创建对IHttpHandler接口的实现,IHttpHandler接口是负责处理请求的,在这些接口的MVC实现中,controller会被找到,action方法会被调用,view会被呈现。结果会被写到Response里面。实例里面的实现非常简单,仅仅是写了一个”Hello”到客户端。

下面是注册我们自己定义的路由:
routes.Add(new Route(“SayHello”, new CustomRouteHandler()));这时运行程序,输入:http://localhost:57400/SayHello,页面会显示”Hello”.

好了,今天的笔记做到这里,后面的笔记是关于Areas和一些有关URL架构的最佳实践。
晚安!

发表评论

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