路由系统非常灵活,但是如果这还不能满足你的 需求,那么,你可以定制路由系统。

创建基于RouteBase 的接口

如果你不喜欢标准路由对象匹配URL的方式,或者你想实现一些特殊的接口,你可以从RouteBase中继承一个类。让你可以控制URL匹配,参数如何解析,URL链接如何生成。从RouteBase继承,你需要实现2个方法:

GetRouteData(HttpContextBase httpContext):这是一个URL匹配工作机制。framework依次在每个RouteTable.Routes调用这个方法。直到其中一个返回non-null值。

GetVirtualPath(RequestContext requestContext, RouteValueDictionary values):  这是生成对外URL的工作机制。

在此,我们演示这种自定义方式,我们创建一个RouteBase类,该类会处理继承来的URL请求。假设我们从一个已经存在的application上移植到一个MVC Framework,但是一些用户已经收藏了之前的URL地址,并且在脚本中硬编码了。我们希望能继续支持老的URL。我们可以通过常规的路由系统处理,但是这里介绍一种更好的方法。

开始之前。我们需要建立一个controller,这个controller能接受之前的request。我们把它命名为LegacyController,如下:

 

  1. using System.Web.Mvc;  
  2.  
  3. namespace URLsAndRoutes.Controllers {  
  4.     public class LegacyController : Controller {  
  5.         public ActionResult GetLegacyURL(string legacyURL) {  
  6.             return View((object)legacyURL);  
  7.         }  
  8.     }  
  9. }  
  10.  

 

这是一个很简单的controller,GetLegacyURL action方法会将参数传递给View。如果我们要实现这个controller,我们需要使用这个方法来接受我们请求的文件,但是现在是简单的把这个URL展现在view中。

注意,上面我们已经为View方法转换了参数,View方法的其中一个重载方法接受一个string参数,该参数指定要显示的view的名字,如果不转变,那么C#编译器会认为我们是调用这个重载函数,为了避免这种情况,我们把它转换成object,这样的话我们可以调用调用另一个重载函数,该重载函数使用默认的view,并且传递view model值。我们也可以使用另一个重载方法,同时指定view name和view model,但这里,我们不希望action方法和view直接显式的关联。

GetLegacyURL.cshtml是和这个action关联的view,显示如下:

 

  1. @model string  
  2. @{  
  3. ViewBag.Title = "GetLegacyURL";  
  4. Layout = null;  
  5. }  
  6. <h2>GetLegacyURL</h2> 
  7. The URL requested was: @Model  

 

这个例子非常简单,我们只是演示自定义路由行为,所以我们不会去创建复杂的action和view。

路由接收到的URL

创建LegacyRoute类,如下:

using System;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

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)

        {

            return null;

        }

    }

}

此类的构造函数,接受一个string数组,表示路由类将要支持的URL,之后,我们会在注册路由的时候指定它。上例中的GetRouteData方法,路由系统会调用它,以此判断是否要处理收到的URL。如果我们不处理这个请求,那么返回null,路由系统继续判断路由表中的下一个记录。如果可以处理,返回一个RouteData类的实例,该实例包含了controller和action变量。

当创建RouteData对象,我们需要在handler中传递值,我们使用标准的MvcRouteHandler类,此类指定了controller和 action的值:

result = new RouteData(this, new MvcRouteHandler());

对大多数的MVC应用程序来说,这个类是必须的,因为此类连结了路由系统和controller/action model。但是你可以实现一个类代替MvcRouteHandler。之后会讲到。

在这个路由实现中,我们路由了任何传递到构造函数的URL请求。当得到一个URL请求,我们为RouteValues的controller和action方法硬编码了一些值,传递了请求的URL作为legacyURL的属性。注意,属性的名字和我们action方法的参数名一致,这样保证了我们生成的值会通过参数传递给action方法。最后一步是用我们的RouteBase子类注册一个新的路由,如下代码:

public static void RegisterRoutes(RouteCollection routes) {

routes.Add(new LegacyRoute(

"~/articles/Windows_3.1_Overview.html",

"~/old/.NET_1.0_Class_Library"));

routes.MapRoute("MyRoute", "{controller}/{action}/{id}",

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

}

我们实现了该类的实例,把我们想要路由的URL传给它。然后加入到RouteCollection集合。现在当我面请求一个我们定义的legacy URL,这个请求由我们的自定义类路由出来,并且定位到我们的controller。如下:

clipboard

生成对外的URL

要支持对外URL的生成,我们需要实现GetVirtualPath方法。同样的,如果不能处理请求的,就通过返回null让路由系统。否则就返回VirtualPathData的实例。

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;

        }

我们使用匿名类型传递了片段变量和其他参数,但是这背后的事情是,路由系统会将这些值转换成RouteValueDictionary对象。所以,比如,我们下面view中的代码:

@Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL =

"~/articles/Windows 3.1 Overview.html" })

和legacyURL属性一起生产的匿名类型转换成RouteValueDictionary类,包含相同的名称。在此类中,我们可以处理一个对外的URL请求,如果有个key命名为legacyURL,并且它的值是之前传递到构造函数的URL值中的一个。我们能进一步指定并检测controller 和action的值,但是对这个例子来说,也已经足够了。

如果我们得到一个匹配,创建一个新 VirtualPathData的实例,在引用中传递给当前对象和对外的URL。我们使用了UrlHelper类的Content方法,转换相对URL,使之能被浏览器处理。可惜,路由系统会预先加了一个额外的/在URL上,所以我们必须小心的处理掉这个/。

创建自定义路由Handler

在路由中,我们依赖的是MvcRouteHandler,因为它连结了routing system和MVC FrameWork。路由系统允许我们自定义我们的路由handler,通过实现IRouteHandler 接口。如下:

using System.Web;

using System.Web.Routing;

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 Proce***equest(HttpContext context)

        {

            context.Response.Write("Hello");

        }

    }

}

IRouteHandler接口的目的是提供一个生成IHttpHandler接口的方法,其中IHttpHandler的作用是处理请求的。在此例中,非常简单,只是输出Hello到Client。我们在定义路由的时候可以注册自定义的handler。如下:

public static void RegisterRoutes(RouteCollection routes) {

routes.Add(new Route("SayHello", new CustomRouteHandler()));

    routes.MapRoute("MyRoute", "{controller}/{action}/{id}",

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

}

当请求URL/SayHello,我们的handler会处理该请求。

clipboard[1]

你可以实现自定义路由handler意味着你自己要对那些常用的方法负责,比如controller和action的处理方式,但是这也给你更多自由。