我需要将文化添加到网址以支持我的asp.net mvc应用程序中的本地化,其中包含以下URL: sample.com/ en / about
sample.com/ zh / product / 2342
我最近将我的应用程序从MVC 5.0升级到5.1但是路由没有按预期工作,因此我创建了一个新的asp.net mvc 5.0测试应用程序,并在几分钟内将文化显示在URL中。但是,只要我将此测试应用程序升级到MVC 5.1,就不再在链接中生成文化,如果您手动将其键入URL,则会出现404错误。
我压缩了我的5.0和5.1测试应用程序here。我需要帮助理解为什么这在MVC 5.1中不起作用以及如何纠正它。也许我对路由的理解是有缺陷的,或者这是5.1的合法错误?
在此测试应用程序中,Home / About操作应用了一个路由属性[Route("about")]
,并且预计当生成该路由的链接时,它应该是localhost/en/about
,而是仅仅localhost/about
1}}。如果在地址栏中输入localhost/en/about
,您将在Mvc 5.1测试应用程序中收到404错误。
以下是在MVC 5.0中有效的相关代码:
public class RouteConfig
{
private const string STR_Culture = "culture";
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{culture}/{controller}/{action}/{id}",
defaults: new { culture = "en", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
foreach (var item in routes)
{
// this works in MVC 5.0
if (item is Route)
{
var route = item as Route;
if (route.Url.IndexOf("{" + STR_Culture + "}") == -1)
route.Url = String.Format("{{{0}}}/{1}", STR_Culture, route.Url);
//AddCulture(route.Defaults);
}
}
}
private static void AddCulture(RouteValueDictionary dictionary)
{
if (dictionary == null)
dictionary = new RouteValueDictionary();
if (dictionary.ContainsKey(STR_Culture) == false)
dictionary.Add(STR_Culture, "en");
}
}
答案 0 :(得分:1)
好的,想通了。 MVC 5.1引入了重大变革。在上面的代码中有一个foreach循环,它动态地更改所有路由URL以附加" {culture} /"占位符。例如,路线about
变为{culture}/about
,依此类推。
这适用于5.0,因为路由的类型为 System.Web.Routing.Route 。在5.1中,他们引入了一堆额外的类。其中一个名为 LinkGenerationRoute ,用于通过属性路由应用的所有路由。此类保留原始 Route 的私有只读引用,该引用是在注册基于属性的路由的routes.MapMvcAttributeRoutes();
的初始调用期间创建的。然后,此类通过将其各自的属性发送到它继承自的基类来克隆Route: Route 。
在foreach循环中,我有效地修改了基本classe的Url,但 NOT 内部引用的 Route 对象 LinkGenerationRoute 坚持着。结果是框架内部现在有两个Route实例,我们只能在创建后修改基本实例。不幸的是,内部 Route (_innerRoute)用于获取虚拟路径,从而导致链接生成错误,因为它在创建后无法修改。
看起来唯一的方法是在每个路径定义中手动添加此占位符。例如
[Route("{culture}/about")]
,[Route("{culture}/contact")]
,[Route("{culture}/product/{productId:int}")]
等等。
在一天结束时,我认为没有必要在此课程中持有对 Route 的内部引用。应该使用当前实例。例如this.GetVirtualPath(requestContext, values);
internal class LinkGenerationRoute : Route
{
private readonly Route _innerRoute; // original route cannot be modified
public LinkGenerationRoute(Route innerRoute)
: base(innerRoute.Url, innerRoute.Defaults, innerRoute.Constraints, innerRoute.DataTokens,
innerRoute.RouteHandler) // original route is effectively cloned by sending individual properties to base class
{
if (innerRoute == null)
{
throw Error.ArgumentNull("innerRoute");
}
_innerRoute = innerRoute;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// Claims no routes
return null;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
// internal route is used for getting the virtual path. fail..
return _innerRoute.GetVirtualPath(requestContext, values);
}
}