In addition to blogging, I'm also using Twitter. Follow me @matthawley Recently, we've been converting over a lot of our ASP.NET Web Form pages to use ASP.NET MVC. While this is no small feat by itself, the underlying problem of having a new Url structure in the site while still supporting legacy Url's was necessary. The idea, is that you hit a page that no longer exists, and you get redirected to the appropriate controller & action within MVC.
Workflow
- A legacy Url is requested from your site. For example, http://www.server.com/Users/Login.aspx
- ASP.NET routing intercepts the request and matches a route from your route collection
- Instead of using the MvcRouteHandler, a LegacyRouteHandler is invoked.
- Using the LegacyRouteHandler, it'll use the route redirection name you specified, generate the MVC Url, and issue a HTTP 301 with the location of http://www.server.com/site/login.
Routing
First, we should define our legacy route class. This is necessary because we need to expose an additional property to enable our routing handler to find the correct MVC route.
1: // The legacy route class that exposes a RedirectActionName
2: public class LegacyRoute : Route {
3: public LegacyRoute(string url, string redirectActionName, IRouteHandler routeHandler)
4: : base(url, routeHandler)
5: {
6: RedirectActionName = redirectActionName;
7: }
8:
9: public string RedirectActionName { get; set; }
10: }
Secondly, we need to define the route handler and associated http handler. The route handler derives from IRouteHandler, and will be the class used when creating your legacy routing. The http handler derives from MvcHandler because it gives us some critical information, like RequestContext. You'll also notice that (while not in the code) you need to copy all of the querystring parameters from the request over. This is a necessary step because the GetVirtualPath method call will take all route data (from RouteData.Values) and try and utilize that when building the Url itself.
1: // The legacy route handler, used for getting the HttpHandler for the request
2: public class LegacyRouteHandler : IRouteHandler {
3: public IHttpHandler GetHttpHandler(RequestContext requestContext) {
4: return new LegacyHandler(requestContext)
5: }
6: }
7:
8: // The legacy HttpHandler that handles the request
9: public class LegacyHandler : MvcHandler {
10: public LegacyHandler(RequestContext requestContext) : base(requestContext) { }
11:
12: protected override void ProcessRequest(HttpContextBase httpContext) {
13: string redirectActionName = ((LegacyRoute)RequestContext.RouteData.Route).RedirectActionName;
14:
15: // ... copy all of the querystring parameters and put them within RouteContext.RouteData.Values
16:
17: VirtualPathData data = RouteTable.Routes.GetVirtualPath(RouteContext, redirectActionName, RouteContext.RouteData.Values);
18:
19: httpContext.Status = "301 Moved Permanently";
20: httpContext.AppendHeader("Location", data.VirtualPath);
21: }
22: }
Lastly, you need to create your routes within the Global.asax file. Remember, that order is necessary when setting up routing.
1: public void RegisterRoutes(RouteCollection routes) {
2: routes.MapRoute("Login", "site/login", new {
3: controller = "Users",
4: action = "DisplayLogin"
5: });
6:
7: routes.Add("", new LegacyRoute(
8: "Users/Login.aspx",
9: "Login",
10: new LegacyRouteHandler()));
11: }
And that's it. When a request comes in, you'll see the following in Fiddler
- A request on "Users/Login.aspx"
- A HTTP 301, with a header "Location" and value of "site/login"
- A request on "site/login"
Final Thoughts
Granted, there's more you can do with this - like creating your own extension methods like MapRoute and doing better handling of finding the route, but this should get you started. Also, I'm writing the code off the top of my head, so there's no guarantee that any of it works as-is. Please let me know if you have any other thoughts.
Lastly, for those wondering why are we using a HTTP 301 status code? Well read up on them. "301 Moved Permanently" indicates "that all future requests should be directed to the given URI." While your end users will not see any difference other than a new URL in the browser, the 301 status code more aimed towards search engines to update their URL's in their indexes.
Click here to download the source code for this example.
86ba17a8-6629-4f2e-8c51-fe37e46eb79c|9|3.9