Demystifying the ASP.NET MVC Project Type

Good Post from Kevin. simply sharing as it is

Most people interested in ASP.NET MVC have at least looked through the Nerd Dinner demo project from Wrox’s Professional ASP.NET MVC 1.0 book. While it's likely that new projects will start using the ASP.NET MVC Web Application Visual Studio project template just as Nerd Dinner does, I like to know what exactly those templates are doing. This knowledge can come in handy when you want to transition legacy applications to MVC or (perhaps more realistically) use MVC for some capabilities in an existing Web Forms application, such as a RESTful API.
This post outlines how to take a standard ASP.NET Web Application project and "transform" it into an MVC project.

Getting Started

I'm starting with a completely stock ASP.NET Web Application project template on a machine that ASP.NET MVC v1.0 was installed on.
New project template using an ASP.NET Web Application templateOnce we’ve created a new project, the first thing to do is add references to the MVC framework and its supporting libraries which include System.Web.Abstractions, System.Web.Mvc and System.Web.Routing.
02_referencesWith the assembly references in place, it's now time to tell Visual Studio that this project is MVC-aware. To do so, close the project within VS, open the corresponding .csproj file in a text editor and add the MVC project type GUID to the <ProjectTypeGuids> element, save and close the file.
Add the MVC Project Type GUID (which starts with 603c0e0b) to the .csproj file so that it looks like this (line-breaks added for the post only):
1.<ProjectTypeGuids>
2.   {603c0e0b-db56-11dc-be95-000d561079b0};
3.   {349c5851-65df-11da-9384-00065b846f21};
4.   {fae04ec0-301f-11d3-bf4b-00c04f79efbc}
5.</ProjectTypeGuids>
With that change completed, re-open the project and VS will now offer MVC-based Master Pages, Views, User Controls and Controller templates when you add new items.
03_mvc-templates
Let's also open up the Default.aspx file added by the VS template and add the following HTML between the <div> tags so we know what page we're looking at when we run the project later:
1.<h1>Hello, from the Default.aspx Web Form!</h1></CODE>

Configuring Routes

Next up, we'll add a Global.asax file to the project, so we can configure routes. This is done normally, by right-clicking on the project name, selecting Add New Item and picking a Global Application Class under the Web category. To make our changes match the MVC project templates, we'll remove all the stub methods the template added for us, except for the Application_Start method, which we'll modify to call the new RegisterRoutes method that we'll also add. For this example, we'll just add the default routes the MVC template includes, but this is the spot you'll customize your routes just like you would in another MVC project. This is what the final code should look like for the Global application class:
01.using System;
02.using System.Web.Mvc;
03.using System.Web.Routing;
04.  
05.namespace DemystifyingMvc
06.{
07.    public class Global : System.Web.HttpApplication
08.    {
09.        public static void RegisterRoutes(RouteCollection routes)
10.        {
11.            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
12.  
13.            routes.MapRoute(
14.                "Default",                                              
15.                "{controller}/{action}/{id}",                           
16.                new { controller = "Home", action = "Index", id = "" }  
17.            );
18.  
19.        }
20.  
21.        protected void Application_Start(object sender, EventArgs e)
22.        {
23.            RegisterRoutes(RouteTable.Routes);
24.        }
25.    }
26.}

Adding Controllers & Views

Now it's time to add a controller and view and wire it all together. We'll add a Controllers and Views folder to the project, like so:
04_add-folders There's nothing special about these folders, they are added using Add > New Folder, but MVC's conventions will search them so it's where we'll place our controllers and views. To make the example line up with the default route, we'll now add a HomeController with an Index action by right-clicking on the Controllers folder and selecting Add > Controller (this 05_add-viewoption is available because of the Project Type GUID we added earlier). The default code added by the template works for this example, so we'll leave it and move on to the view.
To add views for the HomeController we'll add a Home folder under the Views one and then right-click on the Home folder to select Add > View. The Add View dialog will display, and we'll name the view Index, and avoid using a Master Page for this example.
Next, add the following HTML inside the <div> tags in the template so that we can tell when everything is working:
1.<h1>Hello, from the Home/Index view!</h1>
You may notice that the Mvc in the inherits statement of the new view is highlighted by VS, with a tooltip stating "Cannot resolve symbol 'Mvc'". This is because we haven't done any of the configuration steps handled by the MVC project template yet. Let's do that next.

XML Configuration

First, open up the Web.config file in the root of the project so we can fix the assembly reference issues in the view. Under the system.web/compilation/assemblies node, we'll add the System.Web.Abstractions, System.Web.Routing and System.Web.Mvc assemblies so that it looks like this:
01.<assemblies>
02.   <add assembly="System.Core, 
03.      Version=3.5.0.0, Culture=neutral
04.      PublicKeyToken=B77A5C561934E089"/>
05.   <add assembly="System.Data.DataSetExtensions, 
06.      Version=3.5.0.0, Culture=neutral
07.      PublicKeyToken=B77A5C561934E089"/>
08.   <add assembly="System.Web.Extensions, 
09.      Version=3.5.0.0, Culture=neutral,
10.      PublicKeyToken=31BF3856AD364E35"/>
11.   <add assembly="System.Xml.Linq, 
12.      Version=3.5.0.0, Culture=neutral,
13.      PublicKeyToken=B77A5C561934E089"/>
14.   <add assembly="System.Web.Abstractions, 
15.      Version=3.5.0.0, Culture=neutral
16.      PublicKeyToken=31BF3856AD364E35"/>
17.   <add assembly="System.Web.Routing,
18.      Version=3.5.0.0, Culture=neutral,
19.      PublicKeyToken=31BF3856AD364E35"/>
20.   <add assembly="System.Web.Mvc, 
21.      Version=1.0.0.0, Culture=neutral,
22.      PublicKeyToken=31BF3856AD364E35"/>
23.</assemblies>
ASP.NET MVC uses HTTP Modules and Handlers to perform its work, so we'll need to add these to the Web.config file as well. For IIS6 and 7 support, we'll add it to the following three locations.
First up, add the following XML to the system.web/httpModules node:
1.<add name="UrlRoutingModule" 
2.   type="System.Web.Routing.UrlRoutingModule, 
3.      System.Web.Routing, Version=3.5.0.0, Culture=neutral
4.      PublicKeyToken=31BF3856AD364E35"/>
Secondly, add the following XML to the system.webServer/modules node:
1.<remove name="UrlRoutingModule"/>
2.<add name="UrlRoutingModule" 
3.   type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, 
4.      Version=3.5.0.0, Culture=neutral
5.      PublicKeyToken=31BF3856AD364E35"/>
Lastly, add the following XML to the system.webServer/handlers node:
1.<remove name="UrlRoutingHandler"/>
2.<add name="UrlRoutingHandler" preCondition="integratedMode" 
3.   verb="*" path="UrlRouting.axd" 
4.   type="System.Web.HttpForbiddenHandler, System.Web, 
5.      Version=2.0.0.0, Culture=neutral
6.      PublicKeyToken=b03f5f7f11d50a3a"/>
In order to get strongly-typed views working we also need to add a new Web.config file to the Views folder. To do so, right-click on the Views folder and select Add > New Item then select the Web Configuration File template from the Web category. Replace the content of the new Web.config file with the following XML which comes right out of the MVC project template:
01.<?xml version="1.0"?>
02.<configuration>  
03.  <system.web>
04.    <httpHandlers>
05.      <add path="*" verb="*"
06.          type="System.Web.HttpNotFoundHandler"/>
07.    </httpHandlers>
08.  
09.    <!--
10.        Enabling request validation in view pages would cause validation to occur
11.        after the input has already been processed by the controller. By default
12.        MVC performs request validation before a controller processes the input.
13.        To change this behavior apply the ValidateInputAttribute to a
14.        controller or action.
15.    -->
16.    <pages
17.        validateRequest="false"
18.        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, 
19.           System.Web.Mvc, Version=1.0.0.0, Culture=neutral
20.           PublicKeyToken=31BF3856AD364E35"
21.        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, 
22.           Version=1.0.0.0, Culture=neutral
23.           PublicKeyToken=31BF3856AD364E35"
24.        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc,
25.           Version=1.0.0.0, Culture=neutral
26.           PublicKeyToken=31BF3856AD364E35">
27.      <controls>
28.        <add assembly="System.Web.Mvc, Version=1.0.0.0, 
29.           Culture=neutral, PublicKeyToken=31BF3856AD364E35
30.           namespace="System.Web.Mvc" 
31.           tagPrefix="mvc" />
32.      </controls>
33.    </pages>
34.  </system.web>
35.  
36.  <system.webServer>
37.    <validation validateIntegratedModeConfiguration="false"/>
38.    <handlers>
39.      <remove name="BlockViewHandler"/>
40.      <add name="BlockViewHandler" 
41.         path="*" verb="*" 
42.         preCondition="integratedMode" 
43.         type="System.Web.HttpNotFoundHandler"/>
44.    </handlers>
45.  </system.webServer>
46.</configuration>
This sets up some default handlers so that it's not possible to access your views using their file locations (for example /views/home/index.aspx). More importantly, it configures ASP.NET to use the MVC framework's ViewTypeParserFilter, without which strongly-typed MVC views will not work.

Test Run

With all that wonderful XML in place, things should be wired up properly, so now it's time to run the application for the first time. You should be able to build and launch it, but you may be surprised to see the content from the Default.aspx page shown, and not that of the HomeController's Index view. I'll explain this in a moment.
06_run1 It is, however, possible to navigate to /home or /home/index to see the content from the Views/Home/Index.aspx view:
07_run2From this, we can see that MVC is configured and servicing requests. If we want requests to the root of the application to also be handled by MVC, we'll have to make one last change.

MVC the Root

If you want MVC to handle requests to the root of your application (the / path) we need to make some changes to the Default.aspx page so that it matches the setup in the MVC project template. Before we do this, you can now see that having Web Forms and MVC in the same project is pretty straightforward. In fact, this is how the project is setup right now. For instance, we could configure MVC to only handle requests to an /api path and implement a RESTful API using MVC, even if the rest of the application is using Web Forms for the user interface.
That said, if we want MVC to handle requests to the application root, we'll modify the code-behind of the Default.aspx file, so that it looks like this:
01.using System;
02.using System.Web;
03.using System.Web.Mvc;
04.  
05.namespace DemystifyingMvc
06.{
07.    public partial class _Default : System.Web.UI.Page
08.    {
09.        protected void Page_Load(object sender, EventArgs e)
10.        {
11.            var originalPath = Request.Path;
12.            HttpContext.Current.RewritePath(Request.ApplicationPath, false);
13.            IHttpHandler httpHandler = new MvcHttpHandler();
14.            httpHandler.ProcessRequest(HttpContext.Current);
15.            HttpContext.Current.RewritePath(originalPath, false);
16.        }
17.    }
18.}
This code manually invokes the MvcHttpHandler for the application path (since the Request.Path contains “/Default.aspx” when the code runs). This manual execution of the MvcHttpHandler allows our default controller and action to get used instead of the Default.aspx file that IIS executes.
To make it clear what we're doing, you may also want to change the mark-up of the Default.aspx page to match what is in the MVC project template. It helps clarify the page's purpose to developers who may not be familiar with MVC.
1.<%@ Page Language="C#" 
2.   AutoEventWireup="true" 
3.   CodeBehind="Default.aspx.cs" 
4.   Inherits="DemystifyingMvc._Default" %>
5.  
6.<%-- Please do not delete this file. It is used to 
7.     ensure that ASP.NET MVC is activated by IIS when a 
8.     user makes a "/" request to the server. --%>
Save our changes, build and re-run the application. You should now see the content from the Home/Index view when accessing the application root.
08_run3 

What about models?

As you can see, we've now got a fully functioning MVC application, even though we don't have any models. That's because there's nothing special about models in the MVC world, they are just objects and don't have to be in a Models folder, or even present at all if you're writing an exceedingly simple application. Obviously, if your app does much of anything useful, you're going to need them, but they can be put where you'd like, such as in a separate assembly where they can be re-used without any dependency on ASP.NET at all.

Summary

And that, my friends, is the detail that the ASP.NET MVC Web Application project template takes care of for you. It should be noted that there are a few configuration settings used in the MVC template that I haven't replicated here, such as handlers that support *.mvc file paths and even some default page namespace inclusions. I've left these out to show that they aren't necessarily required for MVC to function.
Here's a quick recap on what you need to add to a standard ASP.NET Web Application project to get it to function as an MVC project:
  1. Add MVC-related references (System.Web.Abstractions, System.Web.Mvc, System.Web.Routing)
  2. Add the MVC Project Type GUID to your .csproj file to get VS support for MVC
  3. Add a Global.asax and call the RegisterRoutes method from Application_Start
  4. Add a controller and view
  5. Add the MVC assemblies to the system.web/compilation node in the root Web.config
  6. Add the MVC handlers and modules to the root Web.config
  7. Add a Web.config file to the Views folder that uses System.Web.Mvc.ViewTypeParserFilter for strongly-typed View support
  8. (Optional) Modify the Default.aspx code-behind to invoke the MvcHttpHandler so that requests to the root of your application are handled by MVC