Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Routing legacy requests with and without querystring

(Before starting: I am aware of this and this. I'd like to find a more concise solution -if possible- for a slightly more specific problem)

I'm rewriting an old Webforms app in MVC. As usual, no permalinks should be broken.

I'm using standard {controller}/{action}/{id} routes. Legacy paths are usually SomePage.aspx?ID=xxx, and I have one particular case where Foo.aspx is a list of Bar (new URL: /Bar or /Bar/Index) and Foo.aspx?ID=xxx is the Bar detail (new URL: /Bar/View/xxx)

One possible workaround is adding the following before the Default MapRoute:

routes.MapRoute("Bar View", "Foo.aspx",
                new { controller = "Bar", action = "View" });

And then defining the corresponding action in BarController:

public ActionResult View(int? id)
{
    if (id == null)
        return RedirectToAction("Index");
    return View();
}

There are two problems with this:

  • Now, if I create an ActionLink, it uses the legacy format
  • I'd like to handle this in the routes; making the id nullable and redirecting in the controller is just wrong

I'm fine with mapping the legacy URLs by hand (I don't need a generic solution and there are only about 8 pages)

This is a new project, so I'm not tied to anything.

like image 999
Diego Mijelshon Avatar asked Feb 06 '12 15:02

Diego Mijelshon


1 Answers

I was able to solve this based on Dangerous' idea plus a constraint based on this answer.

My new route table is:

routes.MapRoute("Bar", "Bar/{action}/{id}",
                new
                {
                    controller = "Bar",
                    action = "Index",
                    id = UrlParameter.Optional
                });
routes.MapRoute("Bar View", "Foo.aspx",
                new {controller = "Bar", action = "View"},
                new {id = new QueryStringConstraint()});
routes.MapRoute("Bar Index", "Foo.aspx",
                new { controller = "Bar", action = "Index" });
routes.MapRoute("Default", /*...*/);

And the QueryStringConstraint couldn't be simpler:

public class QueryStringConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route,
                      string parameterName, RouteValueDictionary values,
                      RouteDirection routeDirection)
    {
        return httpContext.Request.QueryString.AllKeys
            .Contains(parameterName, StringComparer.InvariantCultureIgnoreCase);
    }
}
like image 143
Diego Mijelshon Avatar answered Sep 27 '22 16:09

Diego Mijelshon