Резервный маршрут в ASP MVC, если действие не существует - PullRequest
4 голосов
/ 20 декабря 2010

Маршрут по умолчанию в MVC {controller}/{action}/{id} по большей части весьма полезен, так как может установить значение по умолчанию, если входящий URL-адрес не содержит параметр, но есть также способ указать действие по умолчанию для случая, когдадействие не существует на контроллере?

Чего я хочу добиться, так это иметь возможность иметь контроллеры с несколькими конкретными действиями, а затем собственный ловушку, которая использует URL для получения контента из базовой CMS.

Например, контроллер продуктов будет выглядеть примерно так:

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

Где маршрут по умолчанию будет обрабатывать /Products/ProductInfo/54 и т. Д., А URL запроса /Products/Suppliers/Acme будет возвращать ContentFromCms("Suppliers/Acme"); (отправка URL какпараметр был бы лучше, но не нужен, и метод без параметров, в котором я получаю его из запроса, будет в порядке).

В настоящее время я могу придумать два возможных способа достижения этой цели:

Создатьновое ограничение, которое отражает над контроллером, чтобы увидеть, есть ли у него действие с заданным именем, и использовать его в маршруте {controller}/{action}/{id}, что позволяетчтобы иметь более общий вызов, такой как {controller}/{*url}.

Override HandleUnknownAction на контроллере.

Первый подход выглядит так, как будто это был бы довольно окольный способ проверки этого, в то время как дляво-вторых, я недостаточно хорошо знаком с внутренностями MVC и Routing, чтобы знать, как действовать.

Обновление

Ответов не было, но я думал, что будуоставьте мое решение, если кто-нибудь найдет это в будущем, или чтобы люди предложили улучшения / лучшие способы.

Для контроллеров, которым я хотел иметь свой собственный вызов, я дал им интерфейс

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

Iзатем получается из ControllerActionInvoker и переопределяет FindAction.Затем вызывается базовый FindAction, если базовый возвращает ноль, и контроллер снова реализует интерфейс, который я вызываю 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;
    }

Поскольку я также хочу параметры из маршрутизации, я также заменяю RouteData в начале действия Action по умолчанию на контроллере

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

1 Ответ

2 голосов
/ 21 декабря 2010

Вы подходите вполне нормально. Как примечание:

замена

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

с

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

это более строго типизированный способ, чем передача имени интерфейса через строку: что если вы измените пространство имен завтра?

...