Asp.Net MVC路由在5.1中没有按预期工作

时间:2021-05-26 11:21:40

I need to add the culture to the url to support localization in my asp.net mvc application with url's like: sample.com/en/about
sample.com/en/product/2342

我需要将文化添加到网址以支持我的asp.net mvc应用程序中的本地化,其中包含以下URL:sample.com/en/about sample.com/en/product/2342

I recently upgraded my app from MVC 5.0 to 5.1 but the routing did not work as expected so I created a fresh asp.net mvc 5.0 test application and got the culture to show up in the url in a matter of minutes. However as soon as I upgrade this test application to MVC 5.1 the culture is no longer generated in links and if you manually type it into the url you get a 404 error.

我最近将我的应用程序从MVC 5.0升级到5.1但是路由没有按预期工作,因此我创建了一个新的asp.net mvc 5.0测试应用程序,并在几分钟内将文化显示在URL中。但是,只要我将此测试应用程序升级到MVC 5.1,就不再在链接中生成文化,如果您手动将其键入URL,则会出现404错误。

I zipped up my 5.0 and 5.1 test applications here. I need help understanding why this doesn't work in MVC 5.1 and how to correct it. Perhaps my understanding of routing is flawed or this is a legitimate bug with 5.1?

我在这里压缩了我的5.0和5.1测试应用程序。我需要帮助理解为什么这在MVC 5.1中不起作用以及如何纠正它。也许我对路由的理解是有缺陷的,或者这是5.1的合法错误?

In this test application the Home/About action has a routing attribute applied to it [Route("about")] and it's expected that when the link for that route is generated it should be localhost/en/about but instead it's just localhost/about. If you type localhost/en/about into the address bar you'll get a 404 error in the Mvc 5.1 test application.

在这个测试应用程序中,Home / About操作应用了一个路由属性[Route(“about”)],并且预计当生成该路由的链接时,它应该是localhost / en / about,而是它只是localhost /关于。如果在地址栏中输入localhost / en / about,您将在Mvc 5.1测试应用程序中收到404错误。

Here is the relevant code that does work in MVC 5.0:

以下是在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");
    }
}

1 个解决方案

#1


1  

Ok, figured this out. MVC 5.1 has introduced breaking changes. In the code above there is a foreach loop that dynamically changes all routing urls to append the "{culture}/" placeholder. e.g the route about becomes {culture}/about and so on.

好的,想通了。 MVC 5.1引入了重大变革。在上面的代码中有一个foreach循环,它动态地更改所有路由URL以附加“{culture} /”占位符。例如,路线约成为{culture} / about等等。

This works in 5.0 because routes are of type System.Web.Routing.Route. In 5.1 they have introduced a bunch of additional classes. One of which is called LinkGenerationRoute that is used for all routes applied through attribute routing. This class holds on to a private readonly reference of the original Route that was made during the initial call to routes.MapMvcAttributeRoutes(); that registers attribute based routes. Then this class clones that Route by sending its individual properties to the base class that it inherits from: Route.

这适用于5.0,因为路由的类型为System.Web.Routing.Route。在5.1中,他们引入了一堆额外的类。其中一个名为LinkGenerationRoute,用于通过属性路由应用的所有路由。此类保留在初始调用routes期间创建的原始Route的私有只读引用.MapMvcAttributeRoutes();注册基于属性的路线。然后,这个类通过将其各自的属性发送到它继承自的基类来克隆Route:Route。

In the foreach loop I'm effectively modifying the base classe's Url but NOT the internally referenced Route object that LinkGenerationRoute is holding on to. The effect is that there are now two instances of the Route inside the framework and we only have the ability to modify the base one after its created. Unfortunately the internal Route (_innerRoute) is used for getting the virtual path thus causing links to be generated incorrectly because it cannot be modified after its created.

在foreach循环中,我正在有效地修改base classe的Url,但不是LinkGenerationRoute所持有的内部引用的Route对象。结果是框架内部现在有两个Route实例,我们只能在创建后修改基本实例。不幸的是,内部Route(_innerRoute)用于获取虚拟路径,从而导致链接生成错误,因为它在创建后无法修改。

Looks like the only way is to manually add this placeholder in every route definition. e.g. [Route("{culture}/about")], [Route("{culture}/contact")], [Route("{culture}/product/{productId:int}")] and so on.

看起来唯一的方法是在每个路径定义中手动添加此占位符。例如[路线(“{culture} / about”)],[路线(“{culture} / contact”)],[路线(“{culture} / product / {productId:int}”)]等。

At the end of the day I see no point to holding an internal reference to the Route in this class. The current instance should be used. e.g. this.GetVirtualPath(requestContext, values);

在一天结束时,我认为没有必要在本课程中对路线进行内部参考。应该使用当前实例。例如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);
    }
}

#1


1  

Ok, figured this out. MVC 5.1 has introduced breaking changes. In the code above there is a foreach loop that dynamically changes all routing urls to append the "{culture}/" placeholder. e.g the route about becomes {culture}/about and so on.

好的,想通了。 MVC 5.1引入了重大变革。在上面的代码中有一个foreach循环,它动态地更改所有路由URL以附加“{culture} /”占位符。例如,路线约成为{culture} / about等等。

This works in 5.0 because routes are of type System.Web.Routing.Route. In 5.1 they have introduced a bunch of additional classes. One of which is called LinkGenerationRoute that is used for all routes applied through attribute routing. This class holds on to a private readonly reference of the original Route that was made during the initial call to routes.MapMvcAttributeRoutes(); that registers attribute based routes. Then this class clones that Route by sending its individual properties to the base class that it inherits from: Route.

这适用于5.0,因为路由的类型为System.Web.Routing.Route。在5.1中,他们引入了一堆额外的类。其中一个名为LinkGenerationRoute,用于通过属性路由应用的所有路由。此类保留在初始调用routes期间创建的原始Route的私有只读引用.MapMvcAttributeRoutes();注册基于属性的路线。然后,这个类通过将其各自的属性发送到它继承自的基类来克隆Route:Route。

In the foreach loop I'm effectively modifying the base classe's Url but NOT the internally referenced Route object that LinkGenerationRoute is holding on to. The effect is that there are now two instances of the Route inside the framework and we only have the ability to modify the base one after its created. Unfortunately the internal Route (_innerRoute) is used for getting the virtual path thus causing links to be generated incorrectly because it cannot be modified after its created.

在foreach循环中,我正在有效地修改base classe的Url,但不是LinkGenerationRoute所持有的内部引用的Route对象。结果是框架内部现在有两个Route实例,我们只能在创建后修改基本实例。不幸的是,内部Route(_innerRoute)用于获取虚拟路径,从而导致链接生成错误,因为它在创建后无法修改。

Looks like the only way is to manually add this placeholder in every route definition. e.g. [Route("{culture}/about")], [Route("{culture}/contact")], [Route("{culture}/product/{productId:int}")] and so on.

看起来唯一的方法是在每个路径定义中手动添加此占位符。例如[路线(“{culture} / about”)],[路线(“{culture} / contact”)],[路线(“{culture} / product / {productId:int}”)]等。

At the end of the day I see no point to holding an internal reference to the Route in this class. The current instance should be used. e.g. this.GetVirtualPath(requestContext, values);

在一天结束时,我认为没有必要在本课程中对路线进行内部参考。应该使用当前实例。例如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);
    }
}