0

所以我有这个自定义路由,它根据 URL 中的文化设置路由表,但是当我调用 Url.Action(...) 时,它不会生成本地化 URL。任何想法我做错了什么?页面上的文化正在发生变化,我能够确定用户选择了什么语言,但 Url.Action 没有生成本地化 URL。

这是自定义路由,它会更改路由表的值(不确定是否采用这种标准方式):

public class CultureRoute : Route
{
    public CultureRoute(string url, object defaults, object contraints)
        : base(url, new MvcRouteHandler())
    {
        base.Defaults = CreateRouteValueDictionary(defaults);
        base.Constraints = CreateRouteValueDictionary(contraints);
    }
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);
        if (routeData != null)
        {
            var culture = routeData.Values["culture"].ToString();
            var cookie = httpContext.Request.Cookies["culture"];
            var areEqual = false;
            if (cookie == null || cookie.Value == "" || !(areEqual = string.Equals(culture, cookie.Value, StringComparison.OrdinalIgnoreCase)))
            {
                routeData.Values["culture"] = culture;
                httpContext.Response.Cookies.Add(new HttpCookie("culture", culture));                    
            }
            else if (!areEqual)
            {                    
                routeData.Values["culture"] = cookie.Value;
            }
            CultureHelper.SetCurrentCulture(culture);
        }            
        return routeData;
    }
    private static RouteValueDictionary CreateRouteValueDictionary(object values)
    {
        var dictionary = values as IDictionary<string, object>;
        if (dictionary != null)
        {
            return new RouteValueDictionary(dictionary);
        }
        else
        {
            return new RouteValueDictionary(values);
        }
    }  
}

和这个帮助类来设置线程文化:

public class CultureHelper
{
    public static void SetCurrentCulture(string culture)
    {
        var info = CultureInfo.CreateSpecificCulture(culture);
        Thread.CurrentThread.CurrentCulture = info;
        Thread.CurrentThread.CurrentUICulture = info;  
    }
    public static string GetCurrentCulture(bool ignoreRouteData = false)
    {
        if (!ignoreRouteData)
        {
            var routeData = HttpContext.Current.Request.RequestContext.RouteData;
            object culture;
            if (routeData.Values.TryGetValue("culture", out culture))
            {
                return culture.ToString();
            }
        }
        var cookie = HttpContext.Current.Request.Cookies["culture"];
        if (cookie != null && cookie.Value != null)
        {
            return cookie.Value;
        }
        return GetThreadCulture();
    }
    public static string GetThreadCulture()
    {
        var culture = Thread.CurrentThread.CurrentCulture.Name;
        if (culture.IndexOf('-') > -1)
        {
            culture = culture.Substring(0, 2);
        }
        return culture;
    }
}

还有 RouteConfig 类,它从 Global.asax 调用并使用我的自定义路由类设置路由:

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

        routes.Add("Partial", new CultureRoute(
            "{culture}/{cotroller}/partial/{view}",
            new { culture = "ka", controller = "home", action = "partial", view = "" },
            new { culture = "(ka|en)" }));

        routes.Add("Default", new CultureRoute(
            "{culture}/{controller}/{action}/{id}",
            new { culture = "ka", controller = "home", action = "index", id = UrlParameter.Optional },
            new { culture = "(ka|en)" }));
    }
}

但是如果没有这种扩展方法,我将无法生成基于文化的路由,即 Url.Action 不会根据自定义路由类创建的路由表生成 URL:

public static string Action2(this UrlHelper helper, string action)
{ 
    var culture = CultureHelper.GetThreadCulture();
    return helper.Action(action, new { culture = culture });
}
4

2 回答 2

1

要让它在 ActionLink 中构建 URL,您还需要重写名为的反向查找方法GetVirtualPath。这是我如何做到的一个例子(但我是继承RouteBase而不是Route,所以你的可能需要以不同的方式完成)。

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        VirtualPathData result = null;

        if (requestContext.RouteData.IsAreaMatch(this.area))
        {
            var tenant = this.appContext.CurrentTenant;

            // Get all of the pages
            var pages = this.routeUrlPageListFactory.GetRouteUrlPageList(tenant.Id);
            IRouteUrlPageInfo page = null;

            if (this.TryFindMatch(pages, values, out page))
            {
                result = new VirtualPathData(this, page.VirtualPath);
            }
        }

        return result;
    }

    private bool TryFindMatch(IEnumerable<IRouteUrlPageInfo> pages, RouteValueDictionary values, out IRouteUrlPageInfo page)
    {
        page = null;
        Guid contentId = Guid.Empty;

        var action = Convert.ToString(values["action"]);
        var controller = Convert.ToString(values["controller"]);
        var localeId = (int?)values["localeId"];

        if (localeId == null)
        {
            return false;
        }

        if (Guid.TryParse(Convert.ToString(values["id"]), out contentId) && action == "Index")
        {
            page = pages
                .Where(x => x.ContentId.Equals(contentId) &&
                    x.ContentType.ToString().Equals(controller, StringComparison.InvariantCultureIgnoreCase))
                .Where(x => x.LocaleId.Equals(localeId))
                .FirstOrDefault();

            if (page != null)
            {
                return true;
            }
        }

        return false;
    }

我发现由于我的一些路线需要本地化并且在内部我使用 CultureInfo.LCID 而不是文化字符串来识别文化,因此最好将文化解析代码放在Application_BeginRequestGlobal.asax 中的事件中。但如果您仅在内部使用文化字符串,则可能没有必要。

顺便说一句 - 我认为在您的情况下不需要使用 cookie,因为文化可以直接从 URL 派生。这似乎是不必要的开销,尤其是当您考虑到每个请求(包括图像和 javascript 文件)都会传输 cookie 时。更不用说这样做的安全隐患 - 您至少应该加密 cookie 中的值。这是一个示例,展示了如何正确清理 cookie 数据。

于 2013-12-31T07:26:22.827 回答
0

实际上是其他原因导致了不正确的行为。我有一个扩展方法,它生成了切换语言的动作并修改了路线数据。

public static string CultureRoute(this UrlHelper helper, string culture = "ka") 
{
    var values = helper.RequestContext.RouteData.Values;
    string actionName = values["action"].ToString();
    if (values.ContainsKey("culture"))
    {
        values["culture"] = culture;
    }
    else
    {
        values.Add("culture", culture);
    }
    return helper.Action(actionName, HttpContext.Current.Request.QueryString.ToRouteValues());
} 

我把它改成了这个,它可以工作:

public static string CultureRoute(this UrlHelper helper, string culture = "ka") 
{
    var values = helper.RequestContext.RouteData.Values;
    string actionName = values["action"].ToString();
    return helper.Action(actionName, new { culture = culture });
}

我不需要重写GetVirtualPath方法来让它工作..我认为大多数时候我们可以摆脱Route课堂而只是重写GetRouteData,但想听听其他人的想法......

于 2014-01-05T20:14:35.640 回答