ASP.NET MVC 3: Как включить определение действия контроллера из Url

#asp.net-mvc #asp.net-mvc-3

#asp.net-mvc #asp.net-mvc-3

Вопрос:

Удивлен, что я нигде не нахожу этот ответ, как я могу определить, какой контроллер / действие будет вызвано для данного URL в MVC 3?

Обновить

Что я действительно хочу знать: «как я могу определить, какое ControllerAction будет вызвано для данного URL в MVC 3?»…. да

Итак, либо я не знаю о волшебном методе, который это делает:

ControllerActionInfo GetControllerActionInfo(string url)

Или мне придется создать его самому, выполняя все, что делает MVC, когда получает http-запрос.

Моя цель спросить об этом в StackOverflow заключается в том, что я могу сэкономить некоторое время на обратном проектировании этого поведения. Правильный ответ должен напоминать:

Here's how you can do it: и последует некоторый код.

Комментарии:

1. Мне было бы интересно узнать вариант использования для этого. Обычно из приложения вы создаете URL-адрес из контроллера / действия и позволяете платформе обрабатывать маршрутизацию. Зачем вам нужно делать обратное?

2. Вариант использования является совершенно секретным defcon 5.

3. Тогда ответ заключается в переборе набора маршрутов, пока вы не найдете первый, который соответствует HttpContext для данного URL. Способ сделать это засекречен совершенно, совершенно секретно. 🙂 Или вы могли бы посмотреть в исходном коде, aspnet.codeplex.com . Я бы не ожидал, что фреймворк предоставит это, и я не думаю, что это так.

4. Да, я мог бы покопаться в источнике. Даже если бы у меня не было исходного кода, я мог бы декомпилировать. И то же самое мог бы сделать каждый человек, который когда-либо задавал вопрос о переполнении стека.

Ответ №1:

Вы должны использовать фиктивные классы HttpContext и HttpRequest следующим образом:

 public class DummyHttpRequest : HttpRequestBase {

    private string mUrl;

    public DummyHttpRequest(string url) {
        mUrl = url;
    }

    public override string AppRelativeCurrentExecutionFilePath {
        get {
            return mUrl;
        }
    }

    public override string PathInfo {
        get {
            return string.Empty;
        }
    }

}

public class DummyHttpContext : HttpContextBase {

    private string mUrl;

    public DummyHttpContext(string url) {
        mUrl = url;
    }

    public override HttpRequestBase Request {
        get {
            return new DummyHttpRequest(mUrl);
        }
    }

}
  

Редактировать: Кроме того, вы можете расширить DefaultControllerFactory и добавить простой метод для получения нужной информации вместо экземпляра Controller . (Примечание: Это всего лишь пример, вы должны поддерживать другие аспекты, такие как ActionNameAttribute и так далее)

 public class ControllerActionInfo {

    public ControllerActionInfo(Type controllerType, MethodInfo action) {
        ControllerType = controllerType;
        Action = action;
    }

    public Type ControllerType { get; private set; }
    public MethodInfo Action { get; private set; }

}

public class DefaultControllerFactoryEx : DefaultControllerFactory {

    public ControllerActionInfo GetInfo(RequestContext requestContext, string controllerName) {
        Type controllerType = GetControllerType(requestContext, controllerName);

        if (controllerType == null) {
            return null;
        }

        MethodInfo actionMethod = controllerType.GetMethod(requestContext.RouteData.GetRequiredString("action"), BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);

        return new ControllerActionInfo(controllerType, actionMethod);
    }

}
  

Затем используйте следующий фрагмент кода, чтобы получить доступ к контроллеру:

 DummyHttpContext httpContext = new DummyHttpContext("~/home/index");
RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
// IController controller = new DefaultControllerFactory().CreateController(new RequestContext(httpContext, routeData), routeData.GetRequiredString("controller"));
DefaultControllerFactoryEx controllerFactory = new DefaultControllerFactoryEx();

var result = controllerFactory.GetInfo(new RequestContext(httpContext, routeData), routeData.GetRequiredString("controller"));
  

Комментарии:

1. Спасибо. Я думаю, что это намного ближе к тому, что мне нужно. Но как насчет того, чтобы знать, какое действие будет выполнено?

Ответ №2:

Логика для этого находится в System.Web.Mvc.MvcHandler классе, System.Web.Mvc.DefaultControllerFactory class и System.Web.Mvc.ControllerActionInvoker classе. .NET Reflector — ваш друг.

По сути, фреймворк MVC:

  1. Использует отражение для получения всех контроллеров в проекте приложения.

  2. Затем он выполняет что-то вроде IEnumerable<string> controllerNames = controllerTypes.Select(controllerType => controllerType.Name.Replace("Controller",string.Empty)); . Затем он пытается сопоставить первый сегмент пути, {controller} , с одним из этих исправленных имен типов контроллеров (без учета регистра).

  3. Затем он просматривает общедоступные методы этого контроллера, которые имеют возвращаемый тип, который имеет тип ActionResult или некоторую производную. Это сопоставляет имя метода второму сегменту пути, {action} в качестве метода действия, который должен быть вызван.

  4. Если выбранный метод имеет параметр с именем id , то он сопоставляет третий сегмент пути {id} с этим значением и передает его методу. В противном случае необязательный id параметр игнорируется.

  5. Если возвращаемый ActionResult тип является производным от ViewResultBase , то IViewEngine пытается найти соответствующее представление в проекте, используя любые соглашения, указанные для этого механизма просмотра. WebFormViewEngine , например, по умолчанию в проекте ищется ~/Views/{controller}/{action}.ascx , ~/Views/{controller}/{action}.aspx ~/Views/Shared/{action}.ascx , ~/Views/Shared/{action}.aspx ,,,,,,,.

Комментарии:

1. .net reflector больше не мой друг. Telerik JustDecompile — мой новый друг.

2. Этот ответ на самом деле не отвечает на вопрос. Это просто своего рода сообщает, что происходит, учитывая МАРШРУТ по УМОЛЧАНИЮ, который используется в проекте mvc.

3. @RonnieOverby, ваш вопрос касался маршрутизации MVC. Если вам нужна дополнительная информация конкретно о маршрутизации MVC, вам следует проверить ссылку Скотта Гу в конце моего ответа. Если ваш вопрос касается того, как работает маршрутизация Microsoft в целом, то вам следует указать это. Мой ответ иллюстрирует используемый шаблон и то, как он применяется по умолчанию в MVC. Я не уверен, что еще вы хотите

4. @RonnieOverby, возможно, вы захотите ознакомиться с этой статьей 4guysfromrolla, используя ASP.NET Маршрутизация без ASP.NET MVC за помощью в понимании основ ASP.NET маршрутизация, независимо от ASP.NET Платформа MVC . Единственное конкретное отличие маршрутизации в целом от маршрутизации MVC заключается в том, что MVC выдаст исключение, если {controller} и {action} параметры маршрута не определены. (Они могут быть определены неявно, установив значение по умолчанию)