Specifically, does a controller class name have to have the Controller
suffix, and can you change the folder structure in your project if you want to, without breaking things?
Are there other conventions that can be overridden, and how?
Specifically, does a controller class name have to have the Controller
suffix, and can you change the folder structure in your project if you want to, without breaking things?
Are there other conventions that can be overridden, and how?
Most of the conventions are malleable provided you know how the framework operates. Let's tackle two of the biggest conventions:
the "{controller}/{action}/" magic keywords for instantiating controllers from a route
the way the framework searches for Views first in the controller directory, and then in the Shared directory.
Every route that you create is associated with an instance of an MvcRouteHandler
object by default. When the route is matched, that handler is invoked to deal with the incoming request. Here's what the MvcHandler's ProcessRequest looks like:
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
this.AddVersionHeader(httpContext);
string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
IControllerFactory controllerFactory = this.ControllerBuilder.GetControllerFactory();
IController controller = controllerFactory.CreateController(this.RequestContext, requiredString);
if (controller == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { controllerFactory.GetType(), requiredString }));
}
try
{
controller.Execute(this.RequestContext);
}
finally
{
controllerFactory.ReleaseController(controller);
}
}
Notice the hardcoded string "controller". Well, you can replace this handler for any route you'd like if you want to code your own controller-finding logic. Simply do something like this (shameless blog plug):
routes.Add("ImagesRoute",
new Route("graphics/{filename}", new ImageRouteHandler()));
Now when the route is matched, it invokes your own logic, and you can do whatever you please. Incidentally, the reflection that is used to find the XXXXController class with the "Controller" suffix is part of the DefaultControllerFactory
object, invoked in the handler above, and this factory is replaceable.
So, controller picking is one convention that's overridable. What about when it looks for Views when you do a "return View()
" from any controller method? Well here's the constructor for the WebFormViewEngine
, the default view engine of the framework:
public WebFormViewEngine()
{
base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.master", "~/Views/Shared/{0}.master" };
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.aspx", "~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.aspx", "~/Views/Shared/{0}.ascx" };
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
So if you didn't like the convention of looking in the controller directory, and then shared - you could easily extend WebFormViewEngine
(or use an entirely different view engine) and plop it in your global.asax:
ViewEngines.Engines.Add(new MyViewEngine());
One of the amazing things about the MVC framework is how flexible it really is. You can replace almost any part of it with your own logic - and all the code is available to see what they've done.