所以我有这个自定义路由,它根据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 });
}
答案 0 :(得分: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_BeginRequest
中Global.asax中的事件。但如果您仅在内部使用文化字符串,则可能没有必要。
顺便说一句 - 我不认为在你的情况下使用cookie是必要的,因为文化可以直接从URL派生。这似乎是不必要的开销,特别是当您考虑在每个请求(包括图像和javascript文件)上传输cookie时。更不用说这样做的安全隐患 - 你至少应该加密cookie中的值。 Here is an example显示了如何正确清理Cookie数据。
答案 1 :(得分: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
,但是喜欢听别人的想法...