Language-specific Default URL using MVC's Attribute Routing and RouteLocalization.mvc

耗尽温柔 提交于 2019-12-21 17:44:18

问题


I would like to be able to create a succinct language-specific default URL for my website so that when someone browses to:

somesite.com

They get redirected to a language-culture page such as:

  • somesite.com/en-US/
  • somesite.com/sp-MX/
  • somesite.com/fr-FR/

Specifically, I do not want /Home/Index appended to the URLs:

  • somesite.com/en-US/Home/Index
  • somesite.com/sp-MX/Home/Index
  • somesite.com/fr-FR/Home/Index

I am committed to making this site using RouteLocalization.mvc

  • Dresel/RouteLocalization
  • Translating Your ASP.NET MVC Routes

And I would like to use MVC Attribute Routing to the extent feasible.

I am having trouble figuring out how to cause the Start() method redirect to a language-culture specific URL without the addition of something like "index".

Samples of what I have attempted follow:

using RouteLocalization.Mvc;
using RouteLocalization.Mvc.Extensions;
using RouteLocalization.Mvc.Setup;

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.Clear();

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapMvcAttributeRoutes(Localization.LocalizationDirectRouteProvider);

        const string en = "en-us";
        ISet<string> acceptedCultures = new HashSet<string>() { en, "de", "fr", "es", "it" };

        routes.Localization(configuration =>
        {
            configuration.DefaultCulture = en;
            configuration.AcceptedCultures = acceptedCultures;
            configuration.AttributeRouteProcessing = AttributeRouteProcessing.AddAsNeutralAndDefaultCultureRoute;
            configuration.AddCultureAsRoutePrefix = true;
            configuration.AddTranslationToSimiliarUrls = true;
        }).TranslateInitialAttributeRoutes().Translate(localization =>
        {
            localization.AddRoutesTranslation();
        });

        CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = Localization.DetectCultureFromBrowserUserLanguages(acceptedCultures, en);

        var defaultCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;

        routes.MapRoute(
            name: "DefaultLocalized",
            url: "{culture}/{controller}/{action}/{id}",
            constraints: new { culture = @"(\w{2})|(\w{2}-\w{2})" }, 
            defaults: new { culture = defaultCulture, controller = "Home", action = "Index", id = UrlParameter.Optional }
        );

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

And my home controller:

public class HomeController : Controller
{
    [HttpGet]
    [Route]
    public RedirectToRouteResult Start()
    {
        return RedirectToAction("Home", new { culture = Thread.CurrentThread.CurrentCulture.Name });
    }

    [Route("Index", Name = "Home.Index")]
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Contact()
    {
        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

My Global.asax file:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {            
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        AreaRegistration.RegisterAllAreas();
    }
}

回答1:


Redirecting is a separate concern than routing. Since your goal of redirecting any URL to its localized counterpart is a cross-cutting concern your best bet is to make a global filter.

public class RedirectToUserLanguageFilter : IActionFilter
{
    private readonly string defaultCulture;
    private readonly IEnumerable<string> supportedCultures;

    public RedirectToUserLanguageFilter(string defaultCulture, IEnumerable<string> supportedCultures)
    {
        if (string.IsNullOrEmpty(defaultCulture))
            throw new ArgumentNullException("defaultCulture");
        if (supportedCultures == null || !supportedCultures.Any())
            throw new ArgumentNullException("supportedCultures");

        this.defaultCulture = defaultCulture;
        this.supportedCultures = supportedCultures;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var routeValues = filterContext.RequestContext.RouteData.Values;

        // If there is no value for culture, redirect
        if (routeValues != null && !routeValues.ContainsKey("culture"))
        {
            string culture = this.defaultCulture;
            var userLanguages = filterContext.HttpContext.Request.UserLanguages;
            if (userLanguages.Length > 0)
            {
                foreach (string language in userLanguages.SelectMany(x => x.Split(';')))
                {
                    // Check whether language is supported before setting it.
                    if (supportedCultures.Contains(language))
                    {
                        culture = language;
                        break;
                    }
                }
            }

            // Add the culture to the route values
            routeValues.Add("culture", culture);

            filterContext.Result = new RedirectToRouteResult(routeValues);
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Do nothing
    }
}

Usage

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new RedirectToUserLanguageFilter("en", new string[] { "en", "de", "fr", "es", "it" }));
        filters.Add(new HandleErrorAttribute());
    }
}

Note also that your routing is misconfigured. The route setup is run one time per application startup, so setting the default culture to that of the current thread is meaningless. In fact, you should not be setting a default culture at all for your culture route because you want it to miss so your Default route will execute if there is no culture set.

routes.MapRoute(
    name: "DefaultLocalized",
    url: "{culture}/{controller}/{action}/{id}",
    constraints: new { culture = @"(\w{2})|(\w{2}-\w{2})" },
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

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


来源:https://stackoverflow.com/questions/37239919/language-specific-default-url-using-mvcs-attribute-routing-and-routelocalizatio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!