简体   繁体   中英

Localisation Routing in ASP.NET MVC 5 redirecting to home unintentionally

I have be configuring localisation for a website that is already published in ASP.NET MVC 5.

The guide I was following to integrate localisation into my site is from codeproject and I found it quite good: https://www.codeproject.com/Articles/1095786/Route-friendly-localization-of-ASP-NET-MVC

Now to explain the issue, whenever I load the homepage index http://www.example.com/ with no route path it will redirect to the correct language http://www.example.com/en which is the desired result.

My website urls are in the format of http://www.example.com/controller/action . Whenever I visit http://www.example.com/controller/ it will now redirect me back to http://www.example.com/en rather than http://www.example.com/en/controller/index . And when I try typing http://www.example.com/controller/action it throws a 404 error rather than http://www.example.com/en/controller/action .

I think the issue is in the GetLocalistedUrl function in the LocalizationHelper.cs file.

    public static string GetLocalisedUrl(Uri initialUri, IList<string> controllersNames, IList<string> userLangs)
    {
        var res = string.Empty;

        var supportedLocales = GetSupportedLocales();

        var origUrl = initialUri;

        // Dicide requested url to parts
        var cleanedSegments = origUrl.Segments.Select(X => X.Replace("/", "")).ToList();

        // Check is already supported locale defined in route
        // cleanedSegments[0] is empty string, so lang parameter will be in [1] url segment
        var isLocaleDefined = cleanedSegments.Count > 1 && supportedLocales.Contains(cleanedSegments[1]);

        // does request need to be changed
        var isRequestPathToHandle =
            // Url has controller's name part
            (cleanedSegments.Count > 1 && cleanedSegments.Intersect(controllersNames).Count() > 0) ||
            // This condition is for default (initial) route
            (cleanedSegments.Count == 1) ||
            // initial route with lang parameter that is not supported -> need to change it
            (cleanedSegments.Count == 2 && !supportedLocales.Contains(cleanedSegments[1]));     

        if (!isLocaleDefined && isRequestPathToHandle)
        {
            var langVal = "";
            // Get user preffered language from Accept-Language header
            if (userLangs != null && userLangs.Count > 0)
            {
                // For our locale name approach we'll take only first part of lang-locale definition
                var splitted = userLangs[0].Split(new char[] { '-' });
                langVal = splitted[0];
            }

            // If we don't support requested language - then redirect to requested page with default language
            if (!supportedLocales.Contains(langVal))
                langVal = supportedLocales[0];

            var normalisedPathAndQuery = origUrl.PathAndQuery;
            if ((cleanedSegments.Count > 2 &&
                !controllersNames.Contains(cleanedSegments[1]) &&
                controllersNames.Contains(cleanedSegments[2])) ||
                (cleanedSegments.Count == 2) && (!controllersNames.Contains(cleanedSegments[1])))
            {
                // Second segment contains lang parameter, third segment contains controller name
                cleanedSegments.RemoveAt(1);

                // Remove wrong locale name from initial Uri
                normalisedPathAndQuery = string.Join("/", cleanedSegments) + origUrl.Query;
            }

            // Finally, create new uri with language loocale
            res = string.Format("{0}://{1}:{2}/{3}{4}", origUrl.Scheme, origUrl.Host, origUrl.Port, langVal.ToLower(), normalisedPathAndQuery);
        }

        return res;
    }

The function is called in the IHttpModule extension that was created.

public class LangQueryAppenderModule : IHttpModule
{
    /// <summary>
    /// List of supported locales
    /// </summary>
    private readonly IList<string> _supportedLocales;

    /// <summary>
    /// We need to have controllers list to correctly handle situations
    /// when target method name is missed
    /// </summary>
    private readonly IList<string> _controllersNamesList;

    public LangQueryAppenderModule()
    {
        // Get list of supported locales 
        _supportedLocales = LocalizationHelper.GetSupportedLocales();

        // Get controllers list of current project by reflection
        var asmPath = HttpContext.Current.Server.MapPath("~/bin/Central.dll");
        Assembly asm = Assembly.LoadFile(asmPath);

        var controllerTypes = asm.GetTypes()
            .Where(type => typeof(Controller).IsAssignableFrom(type));
        _controllersNamesList = new List<string>();

        foreach (var controllerType in controllerTypes)
        {
            var fullName = controllerType.Name;

            // We need only name part of Controller class that is used in route
            _controllersNamesList.Add(fullName.Substring(0, fullName.Length - "Controller".Length));
        }
    }

    // In the Init function, register for HttpApplication 
    // events by adding your handlers.
    public void Init(HttpApplication application)
    {
        application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
    }

    private void Application_BeginRequest(Object source, EventArgs e)
    {
        try
        {
            HttpApplication app = (HttpApplication)source;
            HttpContext ctx = app.Context;

            // We will redirect to url with defined locale only in case for HTTP GET verb
            // cause we assume that all requests with other verbs will be called from site directly
            // where all the urls created with URLHelper, so it complies with routing rules and will contain "lang" parameter
            if (string.Equals(ctx.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                var localisedUri = LocalizationHelper.GetLocalisedUrl(ctx.Request.Url, _controllersNamesList, ctx.Request.UserLanguages);
              if (!string.IsNullOrEmpty(localisedUri))
                    // Perform redirect action to changed url if it exists
                    ctx.Response.Redirect(localisedUri);
            }
        }
        catch (Exception)
        {
            // Some logging logic could be here
        }
    }

    public void Dispose() { }

}

I fixed the problem, because some of my hyperlinks had caps I had to then use string.ToLower() on any controller name comparisons to ensure it found a matching controller.

  public static string GetLocalisedUrl(Uri initialUri, IList<string> controllersNames, IList<string> userLangs)
    {
        var res = string.Empty;

        var supportedLocales = GetSupportedLocales();


        var origUrl = initialUri;

        // Dicide requested url to parts
        var cleanedSegments = origUrl.Segments.Select(X => X.Replace("/", "")).ToList();

        // Check is already supported locale defined in route
        // cleanedSegments[0] is empty string, so lang parameter will be in [1] url segment
        var isLocaleDefined = cleanedSegments.Count > 1 && supportedLocales.Contains(cleanedSegments[1]);

        cleanedSegments = cleanedSegments.ConvertAll(d => d.ToLower());

        // does request need to be changed
        var isRequestPathToHandle =
            // Url has controller's name part
            (cleanedSegments.Count > 1 && cleanedSegments.Intersect(controllersNames).Count() > 0) ||
            // This condition is for default (initial) route
            (cleanedSegments.Count == 1) ||
            // initial route with lang parameter that is not supported -> need to change it
            (cleanedSegments.Count == 2 && !supportedLocales.Contains(cleanedSegments[1]));     

        if (!isLocaleDefined && isRequestPathToHandle)
        {
            var langVal = "";
            // Get user preffered language from Accept-Language header
            if (userLangs != null && userLangs.Count > 0)
            {
                // For our locale name approach we'll take only first part of lang-locale definition
                var splitted = userLangs[0].Split(new char[] { '-' });
                langVal = splitted[0];
            }

            // If we don't support requested language - then redirect to requested page with default language
            if (!supportedLocales.Contains(langVal))
                langVal = supportedLocales[0];

            var normalisedPathAndQuery = origUrl.PathAndQuery;
            if ((cleanedSegments.Count > 2 &&
                !controllersNames.Contains(cleanedSegments[1].ToLower()) &&
                controllersNames.Contains(cleanedSegments[2].ToLower())) ||
                (cleanedSegments.Count == 2) && (!controllersNames.Contains(cleanedSegments[1])))
            {
                // Second segment contains lang parameter, third segment contains controller name
                cleanedSegments.RemoveAt(1);

                // Remove wrong locale name from initial Uri
                normalisedPathAndQuery = string.Join("/", cleanedSegments) + origUrl.Query;
            }

            // Finally, create new uri with language loocale
            res = string.Format("{0}://{1}:{2}/{3}{4}", origUrl.Scheme, origUrl.Host, origUrl.Port, langVal.ToLower(), normalisedPathAndQuery.ToLower());
        }

        return res;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM