之前写了一篇《MVC路由机制》,今天想了一个实例来加强一下,这才对得起观众。让大家能够系统的理解MVC的路由机制。
扩展思路
从上一篇文章(MVC路由机制)中,我们可以比较容易的发现,UrlRoutingModule通过遍历RouteTable.Routes中的路由对象来获取匹配的RouteData,从而将请求转发到相应的IHttpHandler处理程序。从整个过程中,我们可以从三个地方对MVC的路由进行扩展:Route,IRouteHandler和IRouteConstraint。
简单回顾一下:
Route:它继承实现RouteBase。RouteTable.Routes是一个RouteBase对象集合,可向集合中添加任何RouteBase的子类。所以,我们可以通过创建一个RouteBase的子类,然后将其添加到RouteTable.Routes集合中,以此实现自定义路由规则。
IRouteHandler:UrlRoutingModule在获取到合适的RouteData后,将通过其RouteHandler属性来获取实际的IHttpHandler对象,通过IHttpHandler来处理请求。
IRouteConstraint:IRouteConstraint接口由Route类的Constraints属性使用,用于判断当前的Url是否符合路由的约束条件。
实验场景
对于特殊后缀的处理,比如".do"
1. 网站url只能使用.do结尾。
2.对于".do"结尾的url进行特殊处理,非".do"页面正常处理(这个有一定的应用场景)。
程序实现
1. 我们采取自已写一个Route,然后将它加入RouteTable就可以了。
a. 写route.
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;
namespace RoutStudy.Routes
{
public class DoRoute:RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// 模拟出错:外面的某步操作发生404
if (httpContext.Request.Url.AbsoluteUri.EndsWith(".do"))
{
RouteData rd = new RouteData(this, new MvcRouteHandler());
rd.Values.Add("controller", "RouteExtend");
rd.Values.Add("action", "Do");
return rd;
}
return null;
}
/// <summary>
/// 获取虚拟路径
/// 注意:此方法目前在 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"),才被调用
/// </summary>
/// <param name="requestContext"></param>
/// <param name="values"></param>
/// <returns></returns>
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
}
b. 控制器:
using System.Web.Mvc;
namespace RoutStudy.Controllers
{
public class RouteExtendController : Controller
{
//
// GET: /RouteExtend/
public ActionResult Index()
{
return View();
}
public ActionResult Do()
{
return Content("后缀为DO处理!");
}
}
}
c. 注册路由:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("myDoRoute", new DoRoute());
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "RouteExtend", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
跑一下吧:
出问题了,仔细想想。正常现象,因为我们重写的DoRoute没有处理MVC逻辑。改个路径:http://localhost:4055/abc.do,再刷新。
这是预期的结果。
2. 对于".do"结尾的url进行特殊处理,非".do"页面正常处理
思路:这里我们可以对以".do"结尾的路径专门写一个处理程序来处理;否则,我们走正常MVC流程。
我们知道:UrlRoutingModule在获取到合适的RouteData后,将通过其RouteHandler属性来获取实际的IHttpHandler对象,通过IHttpHandler来处理请求。所以:1. 我们重写IHttpHander,处理".do"后缀程序;2. 将IHttpHander实现扔进路由IRouteHandler中,所以我们要重写IRouteHandler;3. 最终将IRouteHandler实例装进路由对象,再将路由对象进行注册,因此我们需要重写Route。
a. 实现 IHttpHandler和 IRouteHandler接口(包含1、2两步)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
namespace RoutStudy.Handlers
{
/// <summary>
/// 自定义获取IHttpHandler实例的接口
/// </summary>
public class DoRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new DoHandler();
}
private class DoHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("后缀为DO处理!1. 重写:IHttpHandler和 IRouteHandler接口;2. 重写Route,注册DoRouteHandler;3. 在RouteTable中注册Route");
}
#endregion
}
}
}
b. 重写Route
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;
using RoutStudy.Handlers;
namespace RoutStudy.Routes
{
public class DoRouteV2 : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
if (httpContext.Request.Url.AbsoluteUri.EndsWith(".do"))
{
// 注册自定义IRouteHandler 接口
RouteData rd = new RouteData(this, new DoRouteHandler());
return rd;
}
else
{
// 否则使用原MVC路由
RouteData rd = new RouteData(this, new MvcRouteHandler());
rd.Values.Add("controller", "RouteExtend");
rd.Values.Add("action", "Index");
return rd;
}
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
}
c. 注册Route
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("myDoRoute", new DoRouteV2());
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "RouteExtend", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
现在运行一下:
加入".do":
其他:
在MVC中,也可以重写IRouteConstraint 来实际路由的匹配逻辑,这里不细谈。仅贴点代码:
using System.Web;
using System.Web.Routing;
namespace RoutStudy.Constraints
{
/// <summary>
/// 自定义do约束
/// </summary>
public class DoConstraint: IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (httpContext.Request.Url.AbsoluteUri.EndsWith(".do"))
{
return true;
}
return false;
}
}
}
注册:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("myDoRoute", new DoRouteV2());
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "RouteExtend", action = "Index", id = UrlParameter.Optional }
,new DoConstraint()// Parameter defaults
);
}
O啦!