ASP.NET MVC 3:如何从Url确定Controller Action

时间:2011-11-13 15:57:37

标签: asp.net-mvc asp.net-mvc-3

我很惊讶我没有在任何地方找到这个答案,我怎样才能确定在MVC 3中为给定的URL调用哪个Controller / Action?

更新

我真正想知道的是: "如何确定在MVC 3中为给定的URL调用哪个ControllerAction?" ....耶

所以,要么我不知道这样做的神奇方法:

ControllerActionInfo GetControllerActionInfo(string url)

或者,我必须自己创建它,无论MVC在收到http请求时做什么。

我在StackOverflow上询问此问题的目的是我可以节省一些时间逆向工程这种行为。正确的答案应该类似于:

Here's how you can do it:后面会有一些代码。

2 个答案:

答案 0 :(得分: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 :(得分:0)

这个逻辑在System.Web.Mvc.MvcHandler类,System.Web.Mvc.DefaultControllerFactory类和System.Web.Mvc.ControllerActionInvoker类中。 .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