ASP MVC中的后备路由,如果操作不存在

时间:2010-12-20 11:56:49

标签: asp.net asp.net-mvc-2 routing

MVC {controller}/{action}/{id}中的默认路由在很大程度上非常有用,因为如果传入的URL不包含参数但是还有一种指定默认操作的方法,则能够设置默认路由什么时候控制器上不存在动作?

我想要实现的是能够让控制器具有多个特定操作,然后是自己的catchall,它使用url从基本CMS中获取内容。

例如,产品控制器类似于:

public class ProductsController: Controller{
    public ActionResult ProductInfo(int id){...}
    public ActionResult AddProduct(){...}
    public ActionResult ContentFromCms(string url){...} 
}

默认路由将处理/Products/ProductInfo/54等但/Products/Suppliers/Acme的请求网址将返回ContentFromCms("Suppliers/Acme");(将url作为参数发送会更好但不需要,并且无参数方法我从请求中得到它会很好。

目前我可以想到两种可能的方法来实现这一目标:

创建一个新的约束,它反映在一个控制器上,看它是否有一个给定名称的动作,并在{controller}/{action}/{id}路径中使用它,这样我就可以拥有更像{controller}/{*url}这样的更常见的捕获

覆盖控制器上的HandleUnknownAction

第一种方法似乎是一种相当迂回的检查方式,而对于第二种方法,我不知道MVC和路由的内部结构是否足以知道如何继续。

更新

没有任何回复,但我想我会留下我的解决方案,以防任何人在将来发现这一点,或者让人们建议改进/更好的方式

对于想要拥有自己的捕获器的控制器,我给了他们一个接口

interface IHasDefaultController
{
    public string DefaultRouteName { get; }
    System.Web.Mvc.ActionResult DefaultAction();
}

然后我从ControllerActionInvoker派生并覆盖了FindAction。这将调用基础FindAction,如果基数返回null并且控制器触发我使用默认actionname再次调用FindAction的接口。

protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        ActionDescriptor foundAction = base.FindAction(controllerContext, controllerDescriptor, actionName);
        if (foundAction == null && controllerDescriptor.ControllerType.GetInterface("Kingsweb.Controllers.IWikiController") != null)
        {
            foundAction = base.FindAction(controllerContext, controllerDescriptor, "WikiPage");
        }
        return foundAction;
    }

由于我也想要路由中的参数,我还在控制器上默认的Actionresult开头替换RouteData

ControllerContext.RouteData = Url.RouteCollection[DefaultRouteName].GetRouteData(HttpContext);

1 个答案:

答案 0 :(得分:2)

你接近很好。作为旁注:

替换

controllerDescriptor.ControllerType.GetInterface("Kingsweb.Controllers.IWikiController") != null

typeof(Kingsweb.Controllers.IWikiController).IsAssignableFrom(controllerDescriptor.ControllerType)

这是更强类型的方式,然后通过字符串传递接口的名称:如果明天更改命名空间怎么办?...