MVC 5 culture and redirect query string loses value

2k Views Asked by At

this seems like any other question about culture or routing, but I can't figure out why this is happening.

When I try to access an [Authorize] element without being authenticated, I'm redirected to login page with a ReturnUrl param in query string. That's perfect. (boilerplate app)

Now my problem is: if I do the same with localisation within url (from routeConfig) ie: url: "{lang}/{controller}/{action}", I'm being redirected a first time with this parameter, but after been passed by the base controller that redirect me to a localized route. After that, ReturnUrl is null while login page (.cshtml) is loaded...

I've loooked into lots of articles, and tried numerous tricks/options and maybe on the way I did something that's breaks everything, so here are some code:

First RouteConfig :

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

    routes.MapRoute(
       name: "LocalizedDefault",
       url: "{lang}/{controller}/{action}",
       defaults: new { lang = UrlParameter.Optional, controller = "Home", action = "Index" } ,
       constraints: new { lang = "fr-CA|en-US" }
   );

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

}

In BaseController:

protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
    string cultureName = RouteData.Values["lang"] as string;
    // Attempt to read the culture cookie from Request
    if (cultureName == null)
    {
        cultureName = Request.UserLanguages != null && 
        Request.UserLanguages.Length > 0 ? 
                            Request.UserLanguages[0] :
                            null; // obtain it from HTTP header AcceptLanguages
    }

    // Validate culture name
    cultureName = CultureHelper.GetImplementedCulture(StartupMVC.SupportedLangs, cultureName);

    if (RouteData.Values["lang"] as string != cultureName)
    {
        // Force a valid culture in the URL
        RouteData.Values["lang"] = cultureName.ToLowerInvariant(); // lower case too 

        // Redirect user
        //Response.SuppressFormsAuthenticationRedirect = false;
****HERE**** 
if I comment this line, I keep returnUrl but my route 
is like {controller}/{action}/{id} => So default language   
    Response.RedirectToRoute(RouteData.Values);  
otherwise route ok {lang}/{controller}/{action}/{id} but no ReturnUrl 
    }                                                                                
    SetCurrentCultureOnThread(cultureName);
    return base.BeginExecuteCore(callback, state);
}

and a bit of Login.cshtml:

@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
        ... 
}

I hope this is silly and there is some obvious solution you could point out to me. Otherwise, does anyone understand this? and maybe help me to fix it?

PS : if I not being clear enough or you need some more info, please comment what you need! :)

Edit : from NightOwl888's comment, I'd like to add some more questions:

_I kwow there is at least 3 major ways of handling localization (locale in query string, in route, translating the url), but is there any documented or commonly used Best Practices regarding localization and culture?

_How does the .NET framework handles the redirect to login and how does it handles the query string? (since we can not register route with query string values)

2

There are 2 best solutions below

0
On

Added QueryStrings to end of Routes:

if (cultureOnUrl.ToLower() != culture.ToLower())
        {
            try
            {
                var routes = new RouteValueDictionary(filterContext.RouteData.Values);
                foreach (string key in filterContext.HttpContext.Request.QueryString.Keys)
                {
                    routes[key] = filterContext.HttpContext.Request.QueryString[key];
                }
                if (!routes.ContainsKey("lang"))
                    routes.Add("lang", culture);
                else
                    routes["lang"] = culture;  
           filterContext.HttpContext.Response.RedirectToRoute("LocalizedDefault",routes);     
            }
            catch (Exception ex)
            {

            }
            return;
        }
8
On

Specifically tell MVC which route to use. So instead of this:

Response.RedirectToRoute(RouteData.Values);

do this:

Response.RedirectToRoute("LocalizedDefault", RouteData.Values);