Friday, 17 April 2009

Umbraco and ASP.NET MVC

I wanted to be able to implement an Umbraco site with some ASP.NET MVC pages so I set up a basic site as follows...

First off, I set up a clean Umbraco under IIS 7 (Classic mode). As it is classic mode I assume the setup should work with IIS 6.

My test site had a few basic pages provided through Umbraco and 2 MVC controllers - Home and Account. I can access the Umbraco pages through URLs like http://umbracomvc/installing-runway-modules.aspx and the MVC actions through URLs like http://umbracomvc/Account.aspx/LogOn.

I used a custom HttpHandler to process the MVC requests and the UrlRewriteModule that comes with Umbraco to redirect MVC requests to the custom handler.

Web.config

Make the following changes to web.config:

 1. Add the following under system.web/pages
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/></namespaces>
 2. Add the following under system.web/httpModules (more on this later)
<add name="RegisterRoutesModule" type="RegisterRoutesModule"/>
 3. Add the following under system.web/compilation/assemblies
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
mvc.ashx

This is the custom HttpHandler that will process all MVC requests. The actual route is passed to it on the query string - just like Umbraco passes routes to Default.aspx behind the scenes.

Create a file named mvc.ashx in the root of the Umbraco site and paste in the following text:

 

<%@ WebHandler Language="C#" Class="mvc" %>


using System.Web;

using System.Web.Mvc;

 

public class mvc : MvcHttpHandler

{

    protected override void ProcessRequest(HttpContext httpContext)

    {

        string originalPath = httpContext.Request.Path;

        string newPath = httpContext.Request.QueryString["mvcRoute"];

        if (string.IsNullOrEmpty(newPath))

            newPath = "/";

 

        HttpContext.Current.RewritePath(newPath, false);

        base.ProcessRequest(HttpContext.Current);

        HttpContext.Current.RewritePath(originalPath, false);

    }

}


RegisterRoutesModule.cs

We need to set up the MVC routes at some point. We can't do it in Global.asax as Umbraco has its own Global class so I used a custom HttpModule.

Create a file named RegisterRoutesModule.cs in the app_code folder and paste in the following text:

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

 

public class RegisterRoutesModule : IHttpModule

{

    public void Init(HttpApplication application)

    {

        RegisterRoutes(RouteTable.Routes);

    }

 

    public static void RegisterRoutes(RouteCollection routes)

    {

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 

        routes.MapRoute(

            "Default",

            "{controller}.aspx/{action}/{id}",

            new { action = "Index", id = "" }

          );

 

        routes.MapRoute(

          "Root",

          "",

          new { controller = "Home", action = "Index", id = "" }

        );

    }

 

    public void Dispose()

    {

 

    }

}


Views and Controllers

We need some Views and Controllers so create a new ASP.NET MVC project. Copy the Views folder from the new project into the root of the Umbraco site and copy the Controllers folder into the app_code folder of the Umbraco site.

URL Rewriting

Finally we need to redirect incoming requests for MVC pages to the MVC handler. Open up config/UrlRewriting.config in Umbraco and add the following entries:

<add name="MVC_Home_Rewrite"
virtualUrl="^~/Home.aspx/(.*)"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="~/mvc.ashx?mvcRoute=/Home.aspx/$1"
ignoreCase="true" />

<add name="MVC_Account_Rewrite"
virtualUrl="^~/Account.aspx/(.*)"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="~/mvc.ashx?mvcRoute=/Account.aspx/$1"
ignoreCase="true" />

That should be everything. Next I want to create an Umbraco macro that calls MVC actions via a UserControl...

14 comments:

Petr said...

Looks promissing. Thanks

Immo said...

Hi Mark, that's fantastic. I'm for one week thinking about a way to combine Umbraco and MVC worlds. Wasn't shure if it's possible at all. So thanks a lot for sharing your solution. I'll try it out ASAP.

Immo said...

Cool receipe, it works fine here. One issue was the Home.aspx link which requires an additional slash (/). I'm not an expert in URL Rewriting, but I changed it into virtualUrl="^~/Home.aspx(/.*)?" so it works with and without ending slash :) Some more on the Umbraco Forum post MVC with Umbraco

rdinardo said...

I am trying to add ASP.NET MVC controller made in Visual Basic. I tried simply copying the Controllers folder to the Umbraco app_code folder, but the files have to be in C# - makes sense.

However, the DLL that gets created when I deploy my VB MVC Web App should contain all the controller logic and, since it is compiled, it will be okay to use.

I just do not know how to add this DLL. Can anyone help me? Is it a setting in a config file?

Myster said...

I may have done something wrong because my RegisterRoutes function is called for every request, and I get an exception "A route named 'Default' is already in the route collection."

So I hacked in this condition, and it works, but I'm sure it's not ideal

if (routes["Default"] == null){
routes.MapRoute("Default",...

iwache said...

@Myster: Thanks for this simple solution, had this problem while using the umbraco backend. Now it works both, MVC on Frontend and Umbraco Backend.

Great!!!

Sync said...

Hi Mark, very good article.

I've followed the steps from it and integrated MVC in Umbraco with success, but i still have a problem which is critical for me.
Is there any way to inherit the umbraco defined templates in a MVC view?
As i understand the problem is that the umbraco templates become html only at runtime and the doctype properties, as @nodeName, are not recognized "Object null ref" exc being thrown cause of this.

Thank you!

Hector said...
This post has been removed by the author.
Murray Roke said...

I've been using this on a production system for some months without too many troubles, but I recently tried adding [OutputCache...] to a new action on a new controller and some of the legacy controller actions were returning the cached output.

Murray Roke said...

Hi Mark,
I've got a second project I'm using this method with and I've tried upgrading to MVC 2.0 RC2... however I'm currently getting "The resource cannot be found." for the url /Mvc.ashx which makes no sense to me at all, I didn't touch that file, if you have any ideas let me know :-]

Murray Roke said...

regarding my last comment, check out this: http://stackoverflow.com/questions/2473408/using-a-custom-mvchttphandler-v2-0-breaking-change-from-1-0-to-2-0

Saelfaer said...

i'm looking into this post because a client of us is currently working with some MVC applications, and would like to have as mutch re-use of content and controls as possible.

i myself do only know the basics, reading up on mvc, have created a few very small test applications.
never anything that went into production, just examples to learn it all.


but something i notice, you say you start from a new install of umbraco, but mvc only works in 3.5 and up, does this mean you upgraded the umbraco install to a 3.5 environement right? because you didn't mention this i got a bit confused.

Saelfaer said...

I would be very mutch interested in seeing a working copy, as i have some routing issues i assume.

i keep getting the no resource found after the processRequest for the new url occures, i'm having different view names that i know, but am not that familiar with mvc to figure out where it might go wrong.

so if anyone could wrap a small demo of his in a little zip file
that would be awesome.

Saelfaer said...

something i was just thinking about, does anything need to change to the web.config inside the views folder?