Draganov
Draganov

Reputation: 375

How to add route parameter before MVC choses a route

I am trying to add support for different languages to existing MVC 3 application. So far my links were

oldUrl -> myapp.com/Item/Details/5/some-title

And there are links to the website from different places on the web. But now I want to change the URL so the language is included:

newUrl -> kmyapp.com/en/Item/Details/5/some-title

However I want the oldUrl links to be valid. And the question is how do I do this... The easiest way would be to add another route, but I already have too many of them and it is getting kind of ugly, and when I create urls from a page that doesn't have the language the links also don't have the language so I have to do something else then.

Another way would be to override something that stands just before MVC tries to map the request to a specific route, but has already parsed the request url and filled the routeValues dictionary. So I can just check for the language and if it is not included in the route parameters I can add it on my own and call the implementation of the parent class to continue. Is that possible? What should I look at - UrlRoutingModule, MvcHttpHandler or something else? What can I override and how to tell MVC to use my version?

Any other ideas would be also appreciated!

Upvotes: 7

Views: 7110

Answers (2)

Robert Koritnik
Robert Koritnik

Reputation: 105081

There are of course many solutions. I'm going to show you two:

  • one that you control in your application
  • one that you control outside of your application (on IIS)

Solution one - Asp.net MVC routing

Provide routing that covers old and new routing:

routes.MapRoute(
    "New",
    "{lang}/{controller}/{action}/{id}",
    new { lang = "en", controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { lang = "en|de|it|es|fr" }
);
routes.MapRoute(
    "NewEx",
    "{lang}/{controller}/{action}/{id}/{title}",
    new { lang = "en", controller = "Home", action = "Index", id = UrlParameter.Optional, title = UrlParameter.Optional },
    new { lang = "en|de|it|es|fr" }
);

routes.MapRoute(
    "Old",
    "{controller}/{action}/{id}",
    new { lang = "en", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
    "OldEx",
    "{controller}/{action}/{id}/{title}",
    new { lang = "en", controller = "Home", action = "Index", id = UrlParameter.Optional, title = UrlParameter.Optional }
);

As you can see I've provided language default for the old routes as well since it's not present in the URL. Whether you want that or not is your own decision but this kind of routing makes it possible to not duplicate your controller actions. In any case you'd have to define a default language which can be provided this way.

There's a bigger question whether you still want to support old URLs or you'd rather redirect them (HTTP Redirect Permanent Status 301).

Permanent redirection

Change old routes to:

routes.MapRoute(
    "Old",
    "{controllerOld}/{actionOld}/{idOld}",
    new { controller = "Redirect", action = "Permanent", id = UrlParameter.Optional }
);
routes.MapRoute(
    "OldEx",
    "{controllerOld}/{actionOld}/{idOld}/{titleOld}",
    new { controller = "Redirect", action = "Permanent", id = UrlParameter.Optional, title = UrlParameter.Optional }
);

Then write a controller class that does redirection:

public class RedirectController : Controller
{
    public ActionResult Permanent(string controllerOld, string actionOld, string idOld, string titleOld)
    {
        return RedirectToRoutePermanent(new {
            lang = "en",
            controller = controllerOld,
            action = actionOld,
            id = idOld,
            title = titleOld
        });
    }
}

Solution two - IIS URL rewriting module

This solution relies on IIS URL Rewriting module, where you could rewrite any requests without language selection to your preferred default.

I'm not going to wrote how URL Rewriting works here, because there're plenty of web resources with detailed info about that.

Upvotes: 11

swapneel
swapneel

Reputation: 3061

Another way would be to override something that stands just before MVC tries to map the request to a specific route, but has already parsed the request url and filled the routeValues dictionary. So I can just check for the language and if it is not included in the route parameters I can add it on my own and call the implementation of the parent class to continue. Is that possible?

Yes It is possible.

You can Override GetRouteData in RouteBase which will have URL details in it.

public override RouteData GetRouteData(HttpContextBase httpContext)
{
        string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
}

and in global.aspx add below code.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.Add(new MyUrlRoute()); // Add before your default Routes

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );

For more detailed implementation - refer blog url-manipulation-implementing-routebase.html

Upvotes: 4

Related Questions