[Update: If you are using ASP.NET 4 and .NET 4, Microsoft has added direct, built-in support into the Page class (the foundational class for WebForms pages). See Scott Guthrie’s post on this topic: URL Routing with ASP.NET 4 Web Forms (VS 2010 and .NET 4.0 Series).]
I’m a huge fan of ASP.NET Routing. It gained popularity as the part of ASP.NET MVC which channels requests for a given URL to the right controller action. In a wise move, Microsoft moved the routing infrastructure out of ASP.NET MVC and into its own assembly with the release of .NET 3.5 SP1.
With ASP.NET Routing you can construct search engine optimized and human friendly URLs such as these:
http://dotnet.ubbuzz.com/tag/Azure
http://dotnet.ubbuzz.com/user/codinghorror
Here part of the URL (tag or user) selects the page and part of the URL (everything or codinghorror) are effectively query parameters to the page.
This is well documented in the ASP.NET MVC world running on your server – you can’t get anything done without it in MVC. But what about Windows Azure? What if you don’t want ASP.NET MVC? What if you’re a traditional type of person and want all the goodness that comes with what is now called ASP.NET WebForms (aka “normal ASP.NET”)?
In this brief post, I’ll cover how to use ASP.NET routing and ASP.NET WebForms in Azure. The sample project can be downloaded if you want to follow along. Phil Haack has
written a good post on using routing alongside ASP.NET WebForms so I won’t cover too much background information.
How does this change for Azure?
The short answer is that it doesn’t. If you get routing working for IIS 7 in your web app, you can effectively deploy it to Azure. But the steps always felt convoluted to me when reading others’ write-ups on this. So let’s run through converting a Windows Azure Web Role essentially a “stock” ASP.NET WebForms app) to use routing in Azure.
First you’ll need the Azure SDK and Visual Studio tools:
- Next, create a new solution in Visual Studio by choosing Cloud Service->Web and Worker Cloud Service.
- Add a new Global.asax file to your web role project.
- Add a reference to System.Web.Routing and System.Web.Abstractions in your web role project.
- Define a custom class that derives from IRouteHandler which will map URL parameters into the HttpContext for use in your pages:
internal class CustomRoute : IRouteHandler { public CustomRoute(string virtualPath) { VirtualPath = virtualPath; } public string VirtualPath { get; private set; } public IHttpHandler GetHttpHandler(RequestContext requestContext) { foreach ( var aux in requestContext.RouteData.Values ) { HttpContext.Current.Items[aux.Key] = aux.Value; } return BuildManager.CreateInstanceFromVirtualPath( VirtualPath, typeof( Page ) ) as IHttpHandler; } }
- Register these routes in the Application_Start method of your Global.asax:
protected void Application_Start(object sender, EventArgs e) { RouteTable.Routes.Add( "ShowName", new Route( "naming/show/{name}", new CustomRoute( "~/ShowName.aspx" ) ) ); RouteTable.Routes.Add( "CreateAccount", new Route( "account/begin", new CustomRoute( "~/Account.aspx" ) ) ); RouteTable.Routes.Add( "Home", new Route( "home", new CustomRoute( "~/Default.aspx" ) ) ); }
Now if you run your app, you might expect the routing infrastructure to work. Inside the ASP.NET Dev Server (aka cassini) this will likely work. But in the Azure Development Fabric you’ll see this:
The problem is you need to tell IIS 7.5 to get out of the way and let the request get to ASP.NET.
- We’ll define a class to short-circuit the IIS validation
class Iis7RoutingHandler : UrlRoutingHandler { protected override void VerifyAndProcessRequest( IHttpHandler httpHandler, HttpContextBase httpContext) { } }
- Modify the web.config by adding a handler and module to the system.webServer section.
- Finally, we need to recover the data passed to the page. For example, in the sample project we have:route: /naming/show/{name}
example: /naming/show/michael-kennedyHow will our page access the value of name? Recall that our custom route stashes the values in HttpContext.Current.Items. We’ll just pull them back out as follows in our Page_Load method of our ASPX class:LabelName.Text = (string)HttpContext.Current.Items["name"];
That’s it! You can see our routes working in our WebForms app running in Azure (well, technically the screenshot is the dev fabric – but it works in the cloud as well):
Download the source and try it for yourself: AzureRoutingSample.zip (136 KB)