Most suitable method to override on mvc base controller for setting culture

1.6k views Asked by At

I have a base controller in my mvc application that all controllers inherit from. the base controller inherits the abstract class Controller in the System.Web.Mvc namespace, i need to set the culture on each controller action. there are many methods that i can override like OnActionExecuting, Initialize, ExecuteCore, BeginExecute

taking performance into consideration which one is best suited for the job? (the 4 methods above are just examples for a comprehensive list see MSDN)

1

There are 1 answers

2
heymega On BEST ANSWER

We implemented a way of changing the culture based on the user's Accept-Language header but also allowed them to explicity set this themselves and we asked ourselves the same question.

We found the Initialize method the best place to set this as it is called before the other methods and before any action filters. This was important for us as we wanted the action filters to honour the intended culture.

I'm not sure if there would be any performance difference setting the culture in each of these methods. I believe they would just execute at different points in time i.e before action filters, before modelbinding & after tempdata has loaded etc.

I believe we have a helper class somewhere on our source repo that would outline how we did it. Let me know if you would like this and I'll dig it out.

Apologies, I know your question was more performance specific I just wanted to share my experience.

Update

As requested here is the helper class we have used.

public class GlobalisationCookie
{

    public GlobalisationCookie(HttpRequestBase request, HttpResponseBase response)
    {
        _request = request;
        _response = response;

    }

    private readonly HttpRequestBase _request;
    private readonly HttpResponseBase _response;

    //
    // Gets a collection of users languages from the Accept-Language header in their request
    //
    private String[] UserLanguges
    {
        get
        {
            return _request.UserLanguages;
        }

    }

    //
    // A dictionary of cultures the application supports - We point this to a custom webconfig section
    //  This collection is in order of priority
    public readonly Dictionary<string, string> SupportedCultures = new Dictionary<string, string>() { { "en-GB", "English - British" }, { "no", "Norwegian"} }

    //
    //  Summary:
    //       reads the first part of the culture i.e "en", "es"
    //
    private string GetNeutralCulture(string name)
    {
        if (!name.Contains("-")) return name;

        return name.Split('-')[0];
    }



    //
    //  Summary:
    //      returns the validated culture code or default
    //
    private string ValidateCulture(string userCulture)
    {

        if (string.IsNullOrEmpty(userCulture))
        {
            //no culture found - return default
            return SupportedCultures.FirstOrDefault().Key;
        }

        if (SupportedCultures.Keys.Any(x => x.Equals(userCulture, StringComparison.InvariantCultureIgnoreCase)))
        {
            //culture is supported by the application!
            return userCulture;
        }

        //  find closest match based on the main language. for example
        //      if the user request en-GB return en-US as it is still an English language
        var mainLanguage = GetNeutralCulture(userCulture);

        foreach (var lang in SupportedCultures)
        {
            if (lang.Key.StartsWith(mainLanguage)) return lang.Key;
        }

        return SupportedCultures.FirstOrDefault().Key;

    }


    //
    // Called from the OnInitialise method. Sets the culture based on the users cookie or accepted language
    //
    public void CheckGlobalCookie()
    {

        string cultureCode;

        var cookie = _request.Cookies["Globalisation"];

        if (cookie != null)
        {
            cultureCode = cookie.Value;
        }
        else
        {
            cultureCode = UserLanguges != null ? UserLanguges[0] : null;
        }

        //
        // Check we support this culture in our application
        //
        var culture = ValidateCulture(cultureCode);

        //
        // Set the current threads culture
        //
        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

    }

    //
    //  Summary
    //      Called when the user picks there own culture
    //
    public void SetGlobalisationCookie(string culture)
    {

        culture = ValidateCulture(culture);

        var cookie = _request.Cookies["Globalisation"];

        if (cookie != null)
        {

            cookie.Value = culture;
        }
        else
        {

            cookie = new HttpCookie("Globalisation")
            {
                Value = culture,
                Expires = DateTime.Now.AddYears(1)
            };

        }

        cookie.Domain = FormsAuthentication.CookieDomain;
        _response.Cookies.Add(cookie);

        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

    }

}

To use it, just override your controller's Initialize method

protected override void Initialize(RequestContext requestContext)
{

    new GlobalisationCookie(requestContext.HttpContext.Request, requestContext.HttpContext.Response).CheckGlobalCookie();

    base.Initialize(requestContext);
}

The good thing about this approach is the culture will be automatically set the first time the user enters the application based on their browser settings without the user knowing.

It can also fallback to a main language so if a user requests en-US but the application only supports en-GB, it's clever enough to return en-GB as it is still an english language.

Let me know if you have any questions :)