栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

路由和URL中的ASP.NET MVC 5文化

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

路由和URL中的ASP.NET MVC 5文化

这种方法存在多个问题,但是归结为工作流程问题。

  1. CultureController
    的唯一目的是将用户重定向到网站上的另一个页面。请记住,
    RedirectToAction
    会将HTTP 302响应发送到用户的浏览器,这将告诉它在服务器上查找新位置。这是跨网络不必要的往返。
  2. 您正在使用会话状态存储用户的区域性(如果URL中已有该区域性)。在这种情况下,完全不需要会话状态。
  3. 您正在
    HttpContext.Current.Request.UserLanguages
    从用户那里读取,这可能与他们在URL中要求的区域性有所不同。

第三个问题主要是由于微软和Google在如何处理全球化方面存在根本不同的看法。

微软的(原始)观点是,每种文化都应使用相同的URL

UserLanguages
,而浏览器的URL 应该确定网站应显示的语言。

Google的观点是,每种文化都应托管在不同的URL上。如果您考虑一下,这更有意义。希望每个在搜索结果(SERP)中找到您的网站的人都能够以其母语搜索内容。

网站的全球化应该被视为 内容 而不是个性化-您是在向 一群
人而不是个人传播一种文化。因此,使用ASP.NET的任何个性化功能(例如会话状态或cookie)来实现全球化通常是没有意义的-
这些功能会阻止搜索引擎索引本地化页面的内容。

如果您可以简单地通过将用户路由到新的URL来使用户具有不同的文化,则无需担心-
您不需要单独的页面供用户选择其文化,只需在标题中包含一个链接或页脚更改现有页面的区域性,然后所有链接将自动切换到用户选择的区域性(因为MVC
自动重用当前请求中的路由值)。

解决问题

首先,摆脱

CultureController
Application_AcquireRequestState
方法中的代码。

CultureFilter

现在,由于文化是一个跨领域的问题,因此应在中设置当前线程的文化

IAuthorizationFilter
。这样可以确保
ModelBinder
在MVC中使用之前先设置区域性。

using System.Globalization;using System.Threading;using System.Web.Mvc;public class CultureFilter : IAuthorizationFilter{    private readonly string defaultCulture;    public CultureFilter(string defaultCulture)    {        this.defaultCulture = defaultCulture;    }    public void onAuthorization(AuthorizationContext filterContext)    {        var values = filterContext.RouteData.Values;        string culture = (string)values["culture"] ?? this.defaultCulture;        CultureInfo ci = new CultureInfo(culture);        Thread.CurrentThread.CurrentCulture = ci;        Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name);    }}

您可以通过将其注册为全局过滤器来全局设置过滤器。

public class FilterConfig{    public static void RegisterGlobalFilters(GlobalFilterCollection filters)    {        filters.Add(new CultureFilter(defaultCulture: "nl"));        filters.Add(new HandleErrorAttribute());    }}

语言选择

您可以通过链接到当前页面的相同动作和控制器,并将其作为选项包含在页面的页眉或页脚中来简化语言选择

_Layout.cshtml

@{     var routevalues = this.ViewContext.RouteData.Values;    var controller = routevalues["controller"] as string;    var action = routevalues["action"] as string;}<ul>    <li>@Html.Actionlink("Nederlands", @action, @controller, new { culture = "nl" }, new { rel = "alternate", hreflang = "nl" })</li>    <li>@Html.Actionlink("English", @action, @controller, new { culture = "en" }, new { rel = "alternate", hreflang = "en" })</li></ul>

如前所述,页面上的所有其他链接将自动从当前上下文传递一种区域性,因此它们将自动停留在同一区域性中。在这些情况下,没有理由明确地传递文化。

@Actionlink("about", "about", "Home")

使用上述链接,如果当前URL为

/Home/Contact
,则生成的链接将为
/Home/about
。如果当前网址为
/en/Home/Contact
,则链接将生成为
/en/Home/about

默认文化

最后,我们深入您的问题。无法正确生成默认区域性的原因是,路由是2向映射,并且无论您是匹配传入的请求还是生成传出的URL,始终都会赢得第一个匹配项。构建网址时,第一个匹配项是

DefaultWithCulture

通常,您可以简单地通过反转路由顺序来解决此问题。但是,在这种情况下,这将导致传入路由失败。

因此,您遇到的最简单的选择是建立一个自定义路由约束,以在生成URL时处理默认区域性的特殊情况。提供默认区域性时,您只需返回false,这将导致.NET路由框架跳过该

DefaultWithCulture
路由并移至下一个注册的路由(在本例中为
Default
)。

using System.Text.Regularexpressions;using System.Web;using System.Web.Routing;public class CultureConstraint : IRouteConstraint{    private readonly string defaultCulture;    private readonly string pattern;    public CultureConstraint(string defaultCulture, string pattern)    {        this.defaultCulture = defaultCulture;        this.pattern = pattern;    }    public bool Match(        HttpContextbase httpContext,         Route route,         string parameterName,         RoutevalueDictionary values,         RouteDirection routeDirection)    {        if (routeDirection == RouteDirection.UrlGeneration &&  this.defaultCulture.Equals(values[parameterName]))        { return false;        }        else        { return Regex.IsMatch((string)values[parameterName], "^" + pattern + "$");        }    }}

剩下的就是将约束添加到路由配置中。您还应该删除

DefaultWithCulture
路由中的区域性默认设置,因为无论如何,只要URL中提供了区域性,您都只希望使其匹配。
Default
另一方面,该路由应具有一种文化,因为无法通过URL传递该路由。

routes.LowercaseUrls = true;routes.MapRoute(  name: "Errors",  url: "Error/{action}/{pre}",  defaults: new { controller = "Error", action = "Other", pre = UrlParameter.Optional }  );routes.MapRoute(  name: "DefaultWithCulture",  url: "{culture}/{controller}/{action}/{id}",  defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },  constraints: new { culture = new CultureConstraint(defaultCulture: "nl", pattern: "[a-z]{2}") }  );routes.MapRoute(    name: "Default",    url: "{controller}/{action}/{id}",    defaults: new { culture = "nl", controller = "Home", action = "Index", id = UrlParameter.Optional });

属性路由

注意: 本部分仅在使用MVC 5时适用。如果使用早期版本,则可以跳过此部分。

对于AttributeRouting,您可以通过自动为每个动作创建2条不同的路线来简化操作。您需要对每条路线进行一些微调,并将它们添加到使用的相同类结构中

MapMvcAttributeRoutes
。不幸的是,Microsoft决定将这些类型设为内部类型,因此需要Reflection实例化并填充它们。

RouteCollectionExtensions

在这里,我们只使用MVC的内置功能来扫描我们的项目并创建一组路由,然后为区域性插入一个附加的路由URL前缀,然后再将

CultureConstraint
实例添加到我们的MVC
RouteTable中。

还创建了一个单独的路由来解析URL(与AttributeRouting进行路由的方式相同)。

using System;using System.Collections;using System.Linq;using System.Reflection;using System.Web.Mvc;using System.Web.Mvc.Routing;using System.Web.Routing;public static class RouteCollectionExtensions{    public static void MapLocalizedMvcAttributeRoutes(this RouteCollection routes, string urlPrefix, object constraints)    {        MapLocalizedMvcAttributeRoutes(routes, urlPrefix, new RoutevalueDictionary(constraints));    }    public static void MapLocalizedMvcAttributeRoutes(this RouteCollection routes, string urlPrefix, RoutevalueDictionary constraints)    {        var routeCollectionRouteType = Type.GetType("System.Web.Mvc.Routing.RouteCollectionRoute, System.Web.Mvc");        var subRouteCollectionType = Type.GetType("System.Web.Mvc.Routing.SubRouteCollection, System.Web.Mvc");        FieldInfo subRoutesInfo = routeCollectionRouteType.GetField("_subRoutes", BindingFlags.NonPublic | BindingFlags.Instance);        var subRoutes = Activator.CreateInstance(subRouteCollectionType);        var routeEntries = Activator.CreateInstance(routeCollectionRouteType, subRoutes);        // Add the route entries collection first to the route collection        routes.Add((Routebase)routeEntries);        var localizedRouteTable = new RouteCollection();        // Get a copy of the attribute routes        localizedRouteTable.MapMvcAttributeRoutes();        foreach (var routebase in localizedRouteTable)        { if (routebase.GetType().Equals(routeCollectionRouteType)) {     // Get the value of the _subRoutes field     var tempSubRoutes = subRoutesInfo.GetValue(routebase);     // Get the PropertyInfo for the Entries property     PropertyInfo entriesInfo = subRouteCollectionType.GetProperty("Entries");     if (entriesInfo.PropertyType.GetInterfaces().Contains(typeof(IEnumerable)))     {         foreach (RouteEntry routeEntry in (IEnumerable)entriesInfo.GetValue(tempSubRoutes))         {  var route = routeEntry.Route;  // Create the localized route  var localizedRoute = CreateLocalizedRoute(route, urlPrefix, constraints);  // Add the localized route entry  var localizedRouteEntry = CreateLocalizedRouteEntry(routeEntry.Name, localizedRoute);  AddRouteEntry(subRouteCollectionType, subRoutes, localizedRouteEntry);  // Add the default route entry  AddRouteEntry(subRouteCollectionType, subRoutes, routeEntry);  // Add the localized link generation route  var localizedlinkGenerationRoute = CreatelinkGenerationRoute(localizedRoute);  routes.Add(localizedlinkGenerationRoute);  // Add the default link generation route  var linkGenerationRoute = CreatelinkGenerationRoute(route);  routes.Add(linkGenerationRoute);         }     } }        }    }    private static Route CreateLocalizedRoute(Route route, string urlPrefix, RoutevalueDictionary constraints)    {        // Add the URL prefix        var routeUrl = urlPrefix + route.Url;        // Combine the constraints        var routeConstraints = new RoutevalueDictionary(constraints);        foreach (var constraint in route.Constraints)        { routeConstraints.Add(constraint.Key, constraint.Value);        }        return new Route(routeUrl, route.Defaults, routeConstraints, route.DataTokens, route.RouteHandler);    }    private static RouteEntry CreateLocalizedRouteEntry(string name, Route route)    {        var localizedRouteEntryName = string.IsNullOrEmpty(name) ? null : name + "_Localized";        return new RouteEntry(localizedRouteEntryName, route);    }    private static void AddRouteEntry(Type subRouteCollectionType, object subRoutes, RouteEntry newEntry)    {        var addMethodInfo = subRouteCollectionType.GetMethod("Add");        addMethodInfo.Invoke(subRoutes, new[] { newEntry });    }    private static Routebase CreatelinkGenerationRoute(Route innerRoute)    {        var linkGenerationRouteType = Type.GetType("System.Web.Mvc.Routing.linkGenerationRoute, System.Web.Mvc");        return (Routebase)Activator.CreateInstance(linkGenerationRouteType, innerRoute);    }}

然后,只需调用此方法即可

MapMvcAttributeRoutes

public class RouteConfig{    public static void RegisterRoutes(RouteCollection routes)    {        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");        // Call to register your localized and default attribute routes        routes.MapLocalizedMvcAttributeRoutes( urlPrefix: "{culture}/",  constraints: new { culture = new CultureConstraint(defaultCulture: "nl", pattern: "[a-z]{2}") }        );        routes.MapRoute( name: "DefaultWithCulture", url: "{culture}/{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints: new { culture = new CultureConstraint(defaultCulture: "nl", pattern: "[a-z]{2}") }        );        routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { culture = "nl", controller = "Home", action = "Index", id = UrlParameter.Optional }        );    }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/514768.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号