Reputation: 46470
Surprised I'm not finding this answer anywhere, how can I determine what Controller/Action will be invoked for a given URL in MVC 3?
What I really want to know: "how can I determine what ControllerAction will be invoked for a given URL in MVC 3?" ....yeah
So, either I'm not aware of the magic method that does this:
ControllerActionInfo GetControllerActionInfo(string url)
Or, I will have to create it myself doing whatever MVC does when it gets an http request.
My purpose of asking about this on StackOverflow is that I can save some time reverse engineering this behavior. The correct answer should resemble:
Here's how you can do it:
and some code would follow.
Upvotes: 2
Views: 1743
Reputation: 7941
You have to use a dummy HttpContext and HttpRequest classes as follows:
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);
}
}
}
Edit: Also, you can extend the DefaultControllerFactory
and add a simple method to get the desired information instead of an instance of Controller
. (Note: It's merely a sample, you have to support other aspects like ActionNameAttribute
and so on)
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);
}
}
Then, use following code snippet to get access to the controller:
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"));
Upvotes: 1
Reputation: 42246
The logic for this is in the System.Web.Mvc.MvcHandler
class, the System.Web.Mvc.DefaultControllerFactory
class, and the System.Web.Mvc.ControllerActionInvoker
class. .NET Reflector is your friend.
Basically, the MVC framework:
Uses reflection to get all the controllers in the application project.
Then it does something like IEnumerable<string> controllerNames = controllerTypes.Select(controllerType => controllerType.Name.Replace("Controller",string.Empty));
. It then tries to match the first path segment, {controller}
, to one of these sanitized controller type names (case-insensitive).
Then, it looks at this controller's public methods that have a return type that is of type ActionResult
or some derivative. It matches the method name to the second path segment, {action}
, as the action method to be called.
If the selected method has a parameter that is named id
, then it matches the third path segment {id}
to that value, and passes it to the method. Otherwise, the optional id
parameter is ignored.
If the ActionResult
type that is returned is a derivative of ViewResultBase
then the IViewEngine
tries to locate a corresponding view in the project using whatever conventions have been specified for that view engine. The WebFormViewEngine
, for example, looks in the project for ~/Views/{controller}/{action}.ascx
, ~/Views/{controller}/{action}.aspx
, ~/Views/Shared/{action}.ascx
, ~/Views/Shared/{action}.aspx
by default.
Upvotes: 0