栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > 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号