0

We're migrating a legacy app to .NET MVC and have to maintain backwards compatibility for the old URLs. Consider a forum application where URLs look like this

http://example.com/forums?page=ForumsList - shows a list of all forums
http://example.com/forums?page=Threads&forumId=1 - shows list of threads in forum 1
http://example.com/forums?page=Posts&forumId=1&threadId=1 - shows posts in forum 1, thread 1.

I'd like to map those same URLs to actions in my controllers, i.e.

public ActionResult ForumsList(){}
public ActionResult Threads(string forumId){}
public ActionResult Posts(string forumId, string threadId){}

Without duplicating this maintenance nightmare that we had in our legacy system

public ActionResult Index(string page){
    if (page == "Forums"){
         return Forums();
    }
    else if (page == "Threads){
         return Threads(Request.Params["forumId"]);
    }
    else if (page == "Posts"){
         return Posts(Request.Params["forumId"], Request.Params["threadId"]);
    }
    // oh, there's more.  a lot more.
}

All the documentation I can find on custom routing seems to suggest that it can only parse the path portion of a URL, not the params. I tried the following, to no avail:

        routes.MapRoute(
            name: "TestRoutes",
            url: "{controller}/?page={action}",
            defaults: new {controller = "Forums", action = "ForumsList"});

Anything else I can do?

2
  • 1
    Have you looked at the IIS rewrite module, it will intercept requests and either rewite or redirect them to your new routes before they hit the asp.net handler and get routed to the appropriate controller. It supports pretty good regex pattern matching. You could easily rewrite forums?page=Threads&forumId=1 to something like /forums/threads/1 Commented Sep 19, 2014 at 14:19
  • You're example routing is this routes.MapRoute( name: "TestRoutes", url: "{controller}/?page={action}", defaults: new {controller = "Forums", action = "ForumsList"}); Have you tried: routes.MapRoute( name: "TestRoutes", url: "{controller}/?page={action}", defaults: new {controller = "Forums", action = "{action}"); Commented Sep 19, 2014 at 14:34

1 Answer 1

1

Actually, you don't need to do anything special. By default, the modelbinder will attempt to attach anything in the query string as parameters to the action method. You would just have to use one action to handle/proxy all the requests:

public class ForumsController : Controller
{
    public ActionResult Index(string page, int? forumId, int? threadId)
    {
        ...
    }
}

With just the default route of:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Will work just fine.

The main bit of the URL, /forums would match ForumsController, and the default action of Index would be used, since it wasn't specified in the URL. The id parameter is optional. Then, each of the items in the querystring will be set for the same-named parameters of the action. The forumId and threadId parameters need to be nullable ints to allow passing just the page parameter for something like your /forums?page=ForumsList URL.

While you could simply set up your application to work this way in general, I'd actually recommend keeping this particular action as a clearinghouse. First, I'd create a custom route, so you don't have to devote your Index action to this:

routes.MapRoute(
    name: "OldForums",
    url: "forums",
    defaults: new { controller = "OldForums", action = "RedirectToNewURLs" }
);

Then:

public class OldForumsController : Controller
{
    public ActionResult RedirectToNewURLs(string page, int? forumId, int? threadId)
    {
        if (page == "ForumList")
        {
            return RedirectToActionPermanent("Index", "Forums");
        }

        if (page == "Threads")
        {
            return RedirectToActionPermanent("Threads", "Forums", { id = forumId });
        }

        // etc
    }
}

These redirects would point to your new nice forum URLs such as /forums/{id}/threads. So, if a user enters through an old URL, they just get redirected to the new version, and all new users get sent to the good URLs from the start.

EDIT

Actually, I forgot to point out that the one complication with this approach is that you can't actually use the URL /forums for your actual forums index. You could use something like /forums/all to differentiate or just do something like singularize it, i.e. forum. All the other routes would work fine with the forums prefix because only that string itself would be matched by the custom route. Something like forums/1, for example, would fall through to the default route.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.