21 Mar 2010

Editable MVC Routes (Apache Style)

No Comments Uncategorized

Since writing yesterday’s post about what annoys me regarding the limited insight most web developers have in regards to Routing vs Rewriting.  It occurred to me that I might be able to make the difference and benefits between the two more clear, after remembering a post Phil Haack wrote about Editable MVC Routes.

By taking my companies already production ready URL Rewriter that supports runtime-editing of rewriter rules and adding support for routes.  I would essentially be merging together Routing and Rewriting in the same configuration, and making the routes just as editable as the rewriter rules.  By doing this, my hope is that it should illustrate the benefits of having both a Rewriter as well as a Router in your web arsenal, because you can play with both in real time and start to connect in your mind when one is more useful than the other.

I started with the latest release of my companies URL Rewriter and created a contrib project on GitHub that extended the Apache support to also include System.Web.Routing configuration.  The syntax looks similar to the Apache mod_rewrite but specific for routes.  Here is an example of what the config, with both routes and rewriting rules in it, might look like:

RewriteEngine On

#
# Start Rewrite
#

# force to HTTPS
RewriteCond %{HTTPS} (off) [NC]
RewriteRule ^(.*)$ https://www.somesite.com$1 [QSA,L]

# force non-www
RewriteCond %{HTTP_HOST} !^somesite.com$ [NC]
RewriteRule ^(.*)$ http://somesite.com$1 [R=301,L]

# add a trailing slash
RewriteRule ^([^.?]+[^.?/])$ $1/ [R=301,L]

#
# Start Routes
#

RouteDefault controller Home
RouteDefault action Index
RouteUrl {controller}/{action}/{id} "Default"

Notice the standard Route that comes default with an ASP.NET MVC project at the bottom of the traditional rewriter config.  As a planned side-effect of using the Managed Fusion URL Rewriter the routes are completely editable, so they can be changed with out a recompile and with out a restart of the application pool.  So I see those as two huge benefits of using a method like this, on top of illuminating the differences and advantages of using both System.Web.Routing and a URL Rewriter in your application.

The code that I have provided via GitHub provides the following commands that you can use to control your routes:

  • RouteUrl <url>  “<name>”
  • RouteDefault <url-part>  <default>
  • RouteConstraint <url-part>  <regex-constraint>
  • RouteNamespace <namespace>
  • RouteIgnoreUrl <url>

So I encourage you to check this code out and provide feedback, as I aim to continue to grow this code base where I can to make it easier to have your rewriter and your routes in the same place. Because even though I created it for demonstration reasons, I realize that it may have more of a use for others out there that need to support runtime-editable routes in their application.

Note to readers that have just read my last post: You maybe wondering why I compared routes to unchangeable roads in my last post, and then in this one give people the ability to edit them in real time.  Well I did state in the beginning of this post that the main goal of this project above was to illustrate the differences in real-time of routing and rewriting with out the need to recompile.  Also Phil did concede there are times when routes would be better as dynamic code that can be changed without a recompile:

Having said that, there are many situations in which the ability to change an application’s routes without having to recompile the application comes in very handy. This is the situation I find myself in as I build a blog engine where the folks who will install may want to tweak the routes without having to recompile the blog’s source code.

Update: Here is what you put in your Web.config file to get this working. I have included the whole snippet but the important part is the engineType attribute in the XML below:

<managedFusion.rewriter xmlns="http://managedfusion.com/xsd/managedFusion/rewriter">
    <rules engine="Other" engineType="ManagedFusion.Rewriter.Contrib.RoutingApacheEngine, ManagedFusion.Rewriter.Contrib">
        <apache defaultFileName="rewriter.txt"/>
    </rules>
</managedFusion.rewriter>
10 Dec 2009

ASP.NET MVC – 0 to 30 in 30 minutes

2 Comments Uncategorized

I recorded the following presentation for DevReady.NET, a new project that I am working on with a bunch of talented MVC’s and Microsoft employees.

After I get done with editing the presentation, I will post it up to DevReady.NET and my blog.  This will be my first try at making a video presentation for the web, so I look forward to your comments. 

11 May 2009

Creating Your First MVC ViewEngine

13 Comments Uncategorized

A question that I have been hearing a lot lately is:

How do I change the view location in MVC?

But what they really mean to say is:

How do I create a new ViewEngine that uses the view locations of my choosing?

It is actually very simple to do, and once you see it, I think you will agree with my assessment.  The first thing we are going to do to create our custom ViewEngine, is define the paths that we want to use for our master pages, view pages, and shared pages.  I have taken the liberty to define the following paths, you can customize them however you wish:

  • Master Pages:
    ~/Templates
    it use to be ~/Views/Shared or the controllers view
  • View Pages:
    ~/Views
  • Shared Pages:
    ~/Common
    it use to be ~/Views/Shared

The next thing we need to do is create a new class for our ViewEngine, for this example we are going to call it SimpleViewEngine.

public class SimpleViewEngine : VirtualPathProviderViewEngine
{
}

As you might have noticed from above our SimpleViewEngine inherits from VirtualPathProviderViewEngine, this is the root ViewEngine that uses the VirtualPathProvider (VPP). The VPP provides a way for web applications to read files off the file system in their local web application, so it is perfect for what we are doing. If you don’t want a file system based ViewEngine, and maybe want a ViewEngine based from the database, you can use the IViewEngine interface to create your own custom ViewEngine that fits your needs. (MVC is very flexible, by design)

The next thing we need to do is code our paths in to our SimpleViewEngine. We will do this in the constructor, so that they only have to be initialized once for the entire life span of our SimpleViewEngine.

public SimpleViewEngine () 
{
	/* {0} = view name or master page name
	 * {1} = controller name
	 */

	// create our master page location
	MasterLocationFormats = new[] {
		"~/Templates/{0}.master"
	};

	// create our views and common shared locations
	ViewLocationFormats = new[] {
		"~/Views/{1}/{0}.aspx",
		"~/Common/{0}.aspx",
	};

	// create our partial views and common shared locations
	PartialViewLocationFormats = new[] {
		"~/Views/{1}/{0}.ascx",
		"~/Common/{0}.ascx"
	};
}

As you can see the format is pretty straight forward. We create a string[] array with the paths of where our master pages, views, and common views are located. The only thing that we need to do is set place holders in our path so the the VirtualPathProviderViewEngine can replace the master name, view name, and controller name to construct our appropriate path.

  • {0}: is the view name or master page name.
  • {1}: is the controller name.

After we have done the hard part, which honestly wasn’t that hard, of creating the constructor with the paths, we just need to return the view objects from the constructed partial paths. Since we are using the standard ASP.NET Web Form (ASPX/ASCX) rendering engine. We are able to leverage the work already done by the MVC team and just return a new instance of the WebFormView object.

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);
}

Nothing really earth shattering here, just simply filling out the constructor with the proper parameters from our method, and then returning the newly created view. If you wanted to create a view based out of the database, or off your own syntax (meaning not ASP.NET syntax) then you would have to create your own view based off of the IView interface. But for this example we are only concerned with changing where our views are located.

There is one more thing that we need to do, and that is register our new SimpleViewEngine for use in the framework. The registration of view engines is done in the Global.asax, similar to the same way we register new routes.

public static void RegisterViewEngines(ViewEngineCollection viewEngines)
{
	viewEngines.Clear();
	viewEngines.Add(new SimpleViewEngine());
}

public static void RegisterRoutes(RouteCollection routes) { ... }

protected void Application_Start()
{
	RegisterRoutes(RouteTable.Routes);
	RegisterViewEngines(ViewEngines.Engines);
}

So we are now done. You have created a new view engines, defined your own routes, and registered this view engine with the MVC framework. Some other types of paths you may want to consider trying for your applications, using a custom ViewEngine, are special folders for your mobile or Facebook versions of your website.

  • Mobile: ~/Views/{1}/Mobile/{0}.aspx
  • Facebook: ~/Views/{1}/Facebook/{0}.aspx

I told you it was simple and straight forward, and I hope you agree that the MVC team has done an awesome job at providing a very flexible framework for us to tweak and customize it so it fits our applications.