How to implement URL rewriting with Windows Azure?

Viewed 7983

I have an ASP.NET / C# website that's hosted on Windows Azure. The site is a predictions-based social site with a feed of prediction summaries on the main page. If you click on a summary, you're redirected to the details page for that prediction using a simple QueryString.

For example:

http://www.ipredikt.com/details.aspx?id=14

This particular prediction is entitled "Paris Hilton will win the Nobel Peace Prize" so what I'd like to do is implement URL rewriting for my site on Azure as follows:

http://www.ipredikt.com/predictions/14/paris-hilton-will-win-the-nobel-peace-prize

What are some strategies and best practices for doing this? And can someone point me to a good Azure-specific article or two.

The hyphenated title ("paris-hilton-bla-bla") is really just to make the URL more human readable; I don't envision relying on it at all in terms of loading pages. In fact, I'd probably allow duplicate titles since I'll be relying on the prediction ID in the URL.

EDIT:

Forgot to mention that we are NOT based on MVC. We came up w/ our own architecture that uses PageMethods and WebMethods to return JSON to the client. We rely on ASP.NET AJAX to do all of the JSON serialization, and almost all of our UI is built dynamically on the client using jQuery.

EDIT: SOLUTION

Thought I'd share my solution now that I have things up and running.

I made a new class as follows (copied verbatim from somewhere):

public class WebFormRouteHandler<T> : IRouteHandler where T : IHttpHandler, new()
{
   public string VirtualPath { get; set; }

   public WebFormRouteHandler(string virtualPath)
   {
      this.VirtualPath = virtualPath;
   }

   public IHttpHandler GetHttpHandler(RequestContext requestContext)
   {
      return (VirtualPath != null)
          ? (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(VirtualPath, typeof(T))
          : new T();
   }
}

I added the following method to Global.asax. The actual method is MUCH, much longer (it covers every page in the site). You'll see that I support calling the predictions page in lots of different ways: with an id, with an id + title, etc. (The "...fb" versions of pages are for the Facebook app version of my site which use a different MasterPage.)

  public static void RegisterRoutes(RouteCollection routes)
  {
     // Details : 'predictions' Page
     var routeHandlerDetails = new WebFormRouteHandler<Page>("~/details.aspx");
     var routeHandlerDetailsFb = new WebFormRouteHandler<Page>("~/detailsfb.aspx");

     routes.Add(new Route("predictions/{id}", routeHandlerDetails));
     routes.Add(new Route("predictions/{id}/{title}", routeHandlerDetails));

     routes.Add(new Route("fb/predictions/{id}", routeHandlerDetailsFb));
     routes.Add(new Route("fb/predictions/{id}/{title}", routeHandlerDetailsFb));
   }

...and this method is called from Application_Start()

  void Application_Start(object sender, EventArgs e)
  {
     RegisterRoutes(RouteTable.Routes);
  }

Then I added the following to web.config in the system.webServer block:

   <!-- Added for URL Routing -->
   <modules runAllManagedModulesForAllRequests="true">
      <add name="UrlRoutingModule"
           type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
   </modules>

    <!-- Added for URL Routing -->
    <handlers>
      <add name="UrlRoutingHandler"
           preCondition="integratedMode"
           verb="*"
           path="UrlRouting.axd"
           type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
   </handlers>

I also had to exclude the virtual "predictions" directory from authentication (because almost all parts of our site are accessible my non-auth users):

<!-- Url routing -->
<location path="predictions">
   <system.web>
      <authorization>
         <allow users="*" />
      </authorization>
   </system.web>
</location>

Finally, I no longer rely on QueryString string parameters when loading pages, so I had to write some new helper methods. Here's one that extracts a numerical value from the new routing URL (I'll be cleaning this up to only have a single 'return'.):

  public static int GetRouteDataValueAsNumber(HttpRequest request, string propertyName)
  {
     if ((request == null) ||
         (request.RequestContext == null) ||
         (request.RequestContext.RouteData == null) ||
         (request.RequestContext.RouteData.Values[propertyName] == null))
     {
        return -1;
     }

     try
     {
        return System.Convert.ToInt32(request.RequestContext.RouteData.Values[propertyName]);
     }
     catch
     {
     }

     return -1;
  }

Now when I need to read a routing value (like a prediction ID), I do the following:

  long _predictionId = System.Convert.ToInt64(WebAppUtils.GetRouteDataValueAsNumber(Request, "id"));

Works great! Now my site feels like an MVC app with friendly and self-documenting URLs.

Oh, last thing, you also need to enable HTTP Redirection as follows:

Start => Control Panel => Program => Turns Windows Features On => Internet Information Services => World Wide Web Services => Common HTTP Features => (select checkbox for) HTTP Redirection.

2 Answers
Related