Dissecting ASP.NET MVC Framework
Have you already started developing web apps on the new ASP.NET MVC framework? If yes, then you may be wondering how ASP.NET MVC framework works. Here is an walkthrough to dive you deep into the framework‘s internal concepts.
MVC framework is not new but one of the most popular design pattern for UI driven applications. Microsoft has created this wonderful support to allow you develop ASP.NET application based on MVC design patterns.
In order to complete this topic, you must have a working knowledge of ASP.NET Internals. (At least you understand how asp.net works). If not grab a copy of ASP.NET Internals and make yourself familiarized with the core framework.
In this exercise I will use a default MVC project created with ASP.NET MVC 1.0 Web Application project template. The core concept of MVC relies on HttpModule & HttpHandler. Although the article is reasonably lengthy, I have tried to cut code short by removing the part of the code out of discussion and keeping focus on highlighted portion.
When you open up the web.config file in the MVC project, you will notice a reference of HTTPHandler and HttpModule as highlighted below in the system.web/httpHandlers and httpModules.
- HttpHandler (path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler”), this handler reference here has not much importance in the context unless you mapped urls that ends with .mvc
- This is mostly used in IIS 6 and other scenarios where wildcard mapping for ISAPI Extensions does not work well, or you have specific preference to keep urls end with .mvc
- Unless you have above constraint, you can remove the entry above from web.config file.
- HttpModule (name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule”), The most critical part of Routing used by MVC framework in ASP.NET
- Routing was introduced as part of ASP.NET 3.5 extensions to support dynamics url.
- This allows incoming urls to map with custom Http Handler based on URL patterns and various custom filters.
- For routing to work, you must add an assembly reference to System.Web.Routing to your project.
We discussed about routing in little more details below.
web.config
<system.web>
<siteMap/>
<compilation debug="true"/>
<authentication mode="Windows" />
<customErrors mode="Off"/>
<pages>
<controls/>
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
<httpHandlers>
<remove verb="*" path="*.asmx" />
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false" />
<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</httpModules>
</system.web>
The below routing map sample can be found in the default Global.asax file. ASP.NET MVC applications typically register controller routes in Application_Start event of Global.asax.
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes){
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start(){
RegisterRoutes(RouteTable.Routes);
}
}
You make use of RouteCollectionExtensions to simplify the task of route mapping. When you use route.MapRoute, actually you add new Route instance of MVCRouteHandler type. Although Routing is part of ASP.NET 3.5 Extension, MVC is not. So you must add an assembly reference of System.Web.Mvc to get access to this extension & MvcRouteHandler.
RouteCollectionExtensions
public static class RouteCollectionExtensions
{
public static void IgnoreRoute(this RouteCollection routes, string url);
public static void IgnoreRoute(this RouteCollection routes, string url, object constraints);
public static Route MapRoute(this RouteCollection routes, string name, string url);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults);
public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
if (routes == null) {
throw new ArgumentNullException("routes");
}
if (url == null) {
throw new ArgumentNullException("url");
}
Route item = new Route(url, new MvcRouteHandler()){
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints)
};
if ((namespaces != null) && (namespaces.Length > 0)) {
item.DataTokens = new RouteValueDictionary();
item.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, item);
return item;
}
private sealed class IgnoreRouteInternal : Route
{
public IgnoreRouteInternal(string url);
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues);
}
}
URL Routing Module
The key part of routing happens in the PostMapRequestHandler, PostResolveRequestCache(In case of cached results) events of the HttpModule.
1. It finds specific Route by matching the request context. The matching works based on different factors such as the url & input parameters.
2. Then it tries to retreive the RouteHandler from the Route.
3. And this inturn sets the HttpContext.Handler to new instance of IHttpHandler from IRouteHandler.
public class UrlRoutingModule : IHttpModule
{
static UrlRoutingModule();
public UrlRoutingModule();
protected virtual void Dispose();
protected virtual void Init(HttpApplication application);
private void OnApplicationPostMapRequestHandler(object sender, EventArgs e);
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e);
/// <summary>
/// Assigns the HTTP handler for the current request to the context.
/// </summary>
public virtual void PostMapRequestHandler(HttpContextBase context) {
RequestData data = (RequestData)context.Items[_requestDataKey];
if (data != null) {
context.RewritePath(data.OriginalPath);
context.Handler = data.HttpHandler;
}
}
/// <summary>
/// Matches the HTTP request to a route, retrieves the handler for that route, and sets the handler as the HTTP handler for the current request.
/// </summary>
public virtual void PostResolveRequestCache(HttpContextBase context) {
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null) {
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null) {
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0]));
}
if (!(routeHandler is StopRoutingHandler)) {
RequestContext requestContext = new RequestContext(context, routeData);
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null) {
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() }));
}
context.Items[_requestDataKey] = new RequestData { OriginalPath = context.Request.Path, HttpHandler = httpHandler };
context.RewritePath("~/UrlRouting.axd");
}
}
}
/// <summary>
/// Initializes a module and prepares it to handle requests.
/// </summary>
/// <param name="application">
/// An object that provides access to the methods, properties, and events common to all application objects in an ASP.NET application.
/// </param>
protected virtual void Init(HttpApplication application) {
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
}
public RouteCollection RouteCollection { get; set; }
private class RequestData
{
public IHttpHandler HttpHandler { get; set; }
public string OriginalPath { get; set; }
}
}
MVCRouteHandler created new instance of MVCHandler when requested through GetHttpHandler method.
MvcRouteHandler
public class MvcRouteHandler : IRouteHandler
{
// Methods
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new MvcHandler(requestContext);
}
}
Here is magic. ASP.NET Runtime pipeline calles the ProcesRequest method of MvcHandler (HttpHandler)
MvcHandler
public class MvcHandler : System.Web.IHttpHandler, IRequiresSessionState
{
private ControllerBuilder _controllerBuilder;
static MvcHandler();
public MvcHandler(RequestContext requestContext);
protected virtual void ProcessRequest(HttpContext httpContext) {
HttpContextBase base2 = new HttpContextWrapper(httpContext);
this.ProcessRequest(base2);
}
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);
}
}
protected virtual bool IsReusable{
get{return false;}
}
public RequestContext RequestContext { get; private set; }
internal ControllerBuilder ControllerBuilder{
get{
if (this._controllerBuilder == null) {
this._controllerBuilder = ControllerBuilder.Current;
}
return this._controllerBuilder;
}
set{
this._controllerBuilder = value;
}
}
}
ControllerBuilder
public class ControllerBuilder {
private Func<IControllerFactory> _factoryThunk;
private static ControllerBuilder _instance = new ControllerBuilder();
private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public ControllerBuilder() {
SetControllerFactory(new DefaultControllerFactory() {
ControllerBuilder = this
});
}
public static ControllerBuilder Current {
get {
return _instance;
}
}
public HashSet<string> DefaultNamespaces {
get {
return _namespaces;
}
}
public IControllerFactory GetControllerFactory() {
IControllerFactory controllerFactoryInstance = _factoryThunk();
return controllerFactoryInstance;
}
public void SetControllerFactory(IControllerFactory controllerFactory) {
...
}
public void SetControllerFactory(Type controllerFactoryType) {
...
}
}
DefaultControllerFactory
public class DefaultControllerFactory : IControllerFactory {
private IBuildManager _buildManager;
private ControllerBuilder _controllerBuilder;
private ControllerTypeCache _instanceControllerTypeCache;
private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();
public virtual IController CreateController(RequestContext requestContext, string controllerName) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
if (controllerType == null) {
throw new HttpException(404,
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
requestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType)) {
throw new ArgumentException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
controllerType),
"controllerType");
}
try {
return (IController)Activator.CreateInstance(controllerType);
}
catch (Exception ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) {
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
// first search in the current route's namespace collection
object routeNamespacesObj;
Type match;
if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
if (routeNamespaces != null) {
HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
match = GetControllerTypeWithinNamespaces(controllerName, nsHash);
// the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) {
// got a match or the route requested we stop looking
return match;
}
}
}
// then search in the application's default namespace collection
HashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
match = GetControllerTypeWithinNamespaces(controllerName, nsDefaults);
if (match != null) {
return match;
}
// if all else fails, search every namespace
return GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */);
}
private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) {
// Once the master list of controllers has been created we can quickly index into it
ControllerTypeCache.EnsureInitialized(BuildManager);
ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
switch (matchingTypes.Count) {
case 0:
// no matching types
return null;
case 1:
// single matching type
return matchingTypes.First();
default:
// multiple matching types
// we need to generate an exception containing all the controller types
StringBuilder sb = new StringBuilder();
foreach (Type matchedType in matchingTypes) {
sb.AppendLine();
sb.Append(matchedType.FullName);
}
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_ControllerNameAmbiguous,
controllerName, sb));
}
}
public virtual void ReleaseController(IController controller) {
IDisposable disposable = controller as IDisposable;
if (disposable != null) {
disposable.Dispose();
}
}
}
Your Controller
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : System.Web.Mvc.Controller{
public ActionResult Index(){
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About(){
return View();
}
}
}
IController
namespace System.Web.Mvc {
using System.Web.Routing;
public interface IController {
void Execute(RequestContext requestContext);
}
}
ControllerBase
public abstract class ControllerBase : IController {
public ControllerContext ControllerContext {
get;
set;
}
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
Justification = "This property is settable so that unit tests can provide mock implementations.")]
public TempDataDictionary TempData {
get;
set;
}
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
Justification = "This property is settable so that unit tests can provide mock implementations.")]
public ViewDataDictionary ViewData {
get;
set;
}
protected virtual void Execute(RequestContext requestContext) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
VerifyExecuteCalledOnce();
Initialize(requestContext);
ExecuteCore();
}
protected abstract void ExecuteCore();
protected virtual void Initialize(RequestContext requestContext) {
ControllerContext = new ControllerContext(requestContext, this);
}
}
Controller
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter {
public IActionInvoker ActionInvoker {
get {
if (_actionInvoker == null) {
_actionInvoker = CreateActionInvoker();
}
return _actionInvoker;
}
set {
_actionInvoker = value;
}
}
protected virtual IActionInvoker CreateActionInvoker() {
return new ControllerActionInvoker();
}
protected override void ExecuteCore() {
TempData.Load(ControllerContext, TempDataProvider);
try {
string actionName = RouteData.GetRequiredString("action");
if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
HandleUnknownAction(actionName);
}
}
finally {
TempData.Save(ControllerContext, TempDataProvider);
}
}
protected override void Initialize(RequestContext requestContext) {
base.Initialize(requestContext);
Url = new UrlHelper(requestContext);
}
protected virtual void HandleUnknownAction(string actionName) {
throw new HttpException(404, String.Format(CultureInfo.CurrentUICulture,
MvcResources.Controller_UnknownAction, actionName, GetType().FullName));
}
protected virtual IActionInvoker CreateActionInvoker() {
return new ControllerActionInvoker();
}
protected internal ViewResult View() {
return View(null /* viewName */, null /* masterName */, null /* model */);
}
protected internal virtual ViewResult View(string viewName, string masterName, object model) {
if (model != null) {
ViewData.Model = model;
}
return new ViewResult {
ViewName = viewName,
MasterName = masterName,
ViewData = ViewData,
TempData = TempData
};
}
}
ControllerActionInvoker
public class ControllerActionInvoker : IActionInvoker
{
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName){
if (controllerContext == null){
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(actionName)){
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor == null){
return false;
}
FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);
try
{
AuthorizationContext context = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
if (context.Result != null){
this.InvokeActionResult(controllerContext, context.Result);
}
else{
if (controllerContext.Controller.ValidateRequest){
ValidateRequest(controllerContext.HttpContext.Request);
}
IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext context2 = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);
}
}
catch (ThreadAbortException){
throw;
}
catch (Exception exception){
ExceptionContext context3 = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
if (!context3.ExceptionHandled){
throw;
}
this.InvokeActionResult(controllerContext, context3.Result);
}
return true;
}
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {
actionResult.ExecuteResult(controllerContext);
}
protected virtual ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) {
return controllerDescriptor.FindAction(controllerContext, actionName);
}
}
public abstract class ActionResult
{
public abstract void ExecuteResult(ControllerContext context);
}
public abstract class ViewResultBase : ActionResult {
public TempDataDictionary TempData {
get ;
set ;
}
public IView View {
get;
set;
}
public ViewDataDictionary ViewData {
get ;
set ;
}
public ViewEngineCollection ViewEngineCollection {
get {
return _viewEngineCollection ?? ViewEngines.Engines;
}
set {
_viewEngineCollection = value;
}
}
public string ViewName {
get {
return _viewName ?? String.Empty;
}
set {
_viewName = value;
}
}
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
if (String.IsNullOrEmpty(ViewName)) {
ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result = null;
if (View == null) {
result = FindView(context);
View = result.View;
}
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
View.Render(viewContext, context.HttpContext.Response.Output);
if (result != null) {
result.ViewEngine.ReleaseView(context, View);
}
}
protected abstract ViewEngineResult FindView(ControllerContext context);
}
public class ViewResult : ViewResultBase {
private string _masterName;
public string MasterName {
get {
return _masterName ?? String.Empty;
}
set {
_masterName = value;
}
}
protected override ViewEngineResult FindView(ControllerContext context) {
ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
if (result.View != null) {
return result;
}
// we need to generate an exception containing all the locations we searched
StringBuilder locationsText = new StringBuilder();
foreach (string location in result.SearchedLocations) {
locationsText.AppendLine();
locationsText.Append(location);
}
throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
MvcResources.Common_ViewNotFound, ViewName, locationsText));
}
}
public static class ViewEngines {
private readonly static ViewEngineCollection _engines = new ViewEngineCollection {
new WebFormViewEngine()
};
public static ViewEngineCollection Engines {
get {
return _engines;
}
}
}
VirtualPathProviderViewEngine
public abstract class VirtualPathProviderViewEngine : IViewEngine {
protected abstract IView CreatePartialView(ControllerContext controllerContext, string partialPath);
protected abstract IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath);
protected virtual bool FileExists(ControllerContext controllerContext, string virtualPath) {
return VirtualPathProvider.FileExists(virtualPath);
}
public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
if (String.IsNullOrEmpty(viewName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
}
string[] viewLocationsSearched;
string[] masterLocationsSearched;
string controllerName = controllerContext.RouteData.GetRequiredString("controller");
string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);
string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched);
if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) {
return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
}
return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
}
private class ViewLocation {
}
private class AreaAwareViewLocation : ViewLocation {
}
}
public class WebFormViewEngine : VirtualPathProviderViewEngine {
private IBuildManager _buildManager;
public WebFormViewEngine() {
MasterLocationFormats = new[] {
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};
AreaMasterLocationFormats = new[] {
"~/Areas/{2}/Views/{1}/{0}.master",
"~/Areas/{2}/Views/Shared/{0}.master",
"~/Views/Areas/{2}/{1}/{0}.master",
"~/Views/Areas/{2}/Shared/{0}.master"
};
ViewLocationFormats = new[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
AreaViewLocationFormats = new[] {
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
"~/Views/Areas/{2}/{1}/{0}.aspx",
"~/Views/Areas/{2}/{1}/{0}.ascx",
"~/Views/Areas/{2}/Shared/{0}.aspx",
"~/Views/Areas/{2}/Shared/{0}.ascx"
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = AreaViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) {
return new WebFormView(partialPath, null);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
return new WebFormView(viewPath, masterPath);
}
}
public class WebFormView : IView {
public WebFormView(string viewPath, string masterPath) {
if (String.IsNullOrEmpty(viewPath)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewPath");
}
ViewPath = viewPath;
MasterPath = masterPath ?? String.Empty;
}
public virtual void Render(ViewContext viewContext, TextWriter writer) {
if (viewContext == null) {
throw new ArgumentNullException("viewContext");
}
object viewInstance = BuildManager.CreateInstanceFromVirtualPath(ViewPath, typeof(object));
if (viewInstance == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_ViewCouldNotBeCreated,
ViewPath));
}
ViewPage viewPage = viewInstance as ViewPage;
if (viewPage != null) {
RenderViewPage(viewContext, viewPage, writer);
return;
}
ViewUserControl viewUserControl = viewInstance as ViewUserControl;
if (viewUserControl != null) {
RenderViewUserControl(viewContext, viewUserControl, writer);
return;
}
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_WrongViewBase,
ViewPath));
}
private void RenderViewPage(ViewContext context, ViewPage page, TextWriter textWriter) {
if (!String.IsNullOrEmpty(MasterPath)) {
page.MasterLocation = MasterPath;
}
page.ViewData = context.ViewData;
page.SetTextWriter(textWriter);
page.RenderView(context);
}
private void RenderViewUserControl(ViewContext context, ViewUserControl control, TextWriter textWriter) {
if (!String.IsNullOrEmpty(MasterPath)) {
throw new InvalidOperationException(MvcResources.WebFormViewEngine_UserControlCannotHaveMaster);
}
control.ViewData = context.ViewData;
control.SetTextWriter(textWriter);
control.RenderView(context);
}
}
RouteCollection
public class RouteCollection : System.Collections.ObjectModel.Collection<RouteBase>
{
// Fields
private Dictionary<string, RouteBase> _namedMap;
public RouteCollection();
public RouteCollection(VirtualPathProvider virtualPathProvider);
public void Add(string name, RouteBase item);
public RouteData GetRouteData(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (httpContext.Request == null)
{
throw new ArgumentException(RoutingResources.RouteTable_ContextMissingRequest, "httpContext");
}
if (!this.RouteExistingFiles)
{
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
{
return null;
}
}
using (this.GetReadLock())
{
foreach (RouteBase base2 in this)
{
RouteData routeData = base2.GetRouteData(httpContext);
if (routeData != null)
{
return routeData;
}
}
}
return null;
}
}
Route
public class Route : RouteBase
{
/// <summary>
/// Returns information about the requested route.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public override RouteData GetRouteData(HttpContextBase httpContext)
{
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
if (values == null)
{
return null;
}
RouteData data = new RouteData(this, this.RouteHandler);
if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
{
return null;
}
foreach (KeyValuePair<string, object> pair in values)
{
data.Values.Add(pair.Key, pair.Value);
}
if (this.DataTokens != null)
{
foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
{
data.DataTokens[pair2.Key] = pair2.Value;
}
}
return data;
}
private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection)
{
if (this.Constraints != null)
{
foreach (KeyValuePair<string, object> pair in this.Constraints)
{
if (!this.ProcessConstraint(httpContext, pair.Value, pair.Key, values, routeDirection))
{
return false;
}
}
}
return true;
}
public string Url
{
get
{
return (this._url ?? string.Empty);
}
set
{
this._parsedRoute = RouteParser.Parse(value);
this._url = value;
}
}
}