Search code examples
asp.net-mvccookiesasp.net-mvc-routing

How to set cookie value as default value for MapRoute parameter?


Introduction:

I develop multilingual web application. Admin can create new languages (this information is stored in database). So languages are not hardcoded somewhere in code. The user's preferred language is stored in browser cookie.

Now i want to configure url routes from this:

www.host.com/home/about

to this:

www.host.com/{lang}/home/about

RouteConfig.cs looks like:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.LowercaseUrls = true;

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

        foreach (Route r in routes)
        {
            r.Url = "{lang}/" + r.Url;
            if (r.Defaults == null)
                r.Defaults = new RouteValueDictionary();
            r.Defaults.Add("lang", ServiceLocalization.GetLanguageFromBrowserCookie());
        }
    }
}

Parameter {lang} is added to every request url. I need to set default value from cookie. Service method GetLanguageFromBrowserCookie() uses HttpContext.Current.Request.Cookies object to access cookies sent by the client.

But HttpContext.Current.Request object is not accessible at this stage of request-handling pipeline. And i get this error: "Request is not available in this context"

Is there a way to fetch MapRoute parameter with cookie value?


Solution

  • Ok. I figured out how to do this. We need to create custom RouteHandler to process request url with extra logic.

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.LowercaseUrls = true;
    
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            ).RouteHandler = new CustomRouteHandler();
        }
    }
    public class CustomRouteHandler : MvcRouteHandler
    {
        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            var route = (Route)requestContext.RouteData.Route;
    
            if (!route.Url.Contains("{lang}"))
                route.Url = "{lang}/" + route.Url;
            if (route.Defaults == null)
            {
                route.Defaults = new RouteValueDictionary();
                route.Defaults.Add("lang", ServiceLocalization.GetLanguageFromBrowserCookie().CodeName);
            }
            else
            {
                route.Defaults["lang"] = ServiceLocalization.GetLanguageFromBrowserCookie().CodeName;
            }
    
            return base.GetHttpHandler(requestContext);
        }
    }
    

    I create CustomRouteHandler and pass it to Default route. In GetHttpHandler method we can access current HttpRequest and get any data from request.