.NET MVC Core 3.1 本地化 UrlHelper 不采用当前文化

.NET MVC Core 3.1 Localization UrlHelper doesn't take the current culture

我正在将 .Net 4.7 应用程序转换为 .Net Core 3.1。我正在更新本地化部分。我遵循了一些示例,例如 localization-in-asp-net-core-3-1-mvc.

它工作正常,但我没有找到使 UrlHelper.Action 在没有精确文化的情况下工作的方法。 我想自动设置文化参数。它应该来自 userclaims,或者以前的请求文化,或者默认文化。

例如,如果 URL 是“/Home/Contact”,则 UrlHelper 或 HtmlHelper 生成的 link 将是 /Home/About。 如果当前 URL 是“/en/Home/Contact”,则 link 将生成为“/en/Home/About”。 如果用户通过身份验证,它应该是“/userCulture/Home/About”。

但是我不能强制我的路由模板只有“{culture=en}/{controller=Home}/{action=Welcome}/{id?}”因为 root url 必须可以访问并且 API url应该保持“api/somestufff”。

Startup.cs :

var supportedCultures = CultureHelper.Cultures.Select(a => new CultureInfo(a)).ToArray();
var requestLocalizationOptions = new RequestLocalizationOptions();
requestLocalizationOptions.SupportedCultures = supportedCultures;
services.AddLocalization();
services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture("fr", "fr");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
    options.RequestCultureProviders.Insert(0, new RouteValueRequestCultureProvider() { Options = requestLocalizationOptions });
});

//......................................

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "culture-route", pattern: "{culture=en}/{controller=Home}/{action=Index}/{id?}");
    endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
});

控制器:

[Route("api/somestufff")]
public async Task<IActionResult> Somestufff()
{ ... }

[Route("{culture:length(2)}/items/{number:int}/{permalink?}")]
public async Task<IActionResult> DisplayItem(int number, string permalink)
{ ... }

剃刀页面:

Url.Action("DisplayItem", "MyController",new { culture = ViewBag.Culture as string, number = 123, permalink = "permalink1235" }) 
// => /en/items/123/permalink1235 OK

Url.Action("DisplayItem", "MyController",new { number = 123, permalink = "permalink1235"  }) 
// => /en/MyController/DisplayItem?number=123&permalink=permalink1235 KO

有没有办法让 Urlhelper 添加当前上下文中缺少的文化?

由于您使用的是 .NET Core,因此您应该使用 Tag helpers.

替换 Url.Action 方法

通过使用标签助手,您可以使用这样的东西:

<a asp-controller="MyController" asp-action="DisplayItem" asp-route-number="123">Click</a>

这显然不能解决您路线中的culture,我们将在一分钟内解决。

当前处理a标签上的asp-属性的标签持有者由Microsoft.AspNetCore.Mvc.TagHelpers中的AnchorTagHelperclass完成。

我们可以继承 class 并添加有关处理 culture.

的功能
  1. /TagHelpers
  2. 下的 MVC 项目中创建名为 CultureAnchorTagHelper 的 class
  3. 填写内容如下:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace YourMvcName.TagHelpers
{
    [HtmlTargetElement("a", Attributes = ActionAttributeName)]
    [HtmlTargetElement("a", Attributes = ControllerAttributeName)]
    [HtmlTargetElement("a", Attributes = AreaAttributeName)]
    [HtmlTargetElement("a", Attributes = PageAttributeName)]
    [HtmlTargetElement("a", Attributes = PageHandlerAttributeName)]
    [HtmlTargetElement("a", Attributes = FragmentAttributeName)]
    [HtmlTargetElement("a", Attributes = HostAttributeName)]
    [HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
    [HtmlTargetElement("a", Attributes = RouteAttributeName)]
    [HtmlTargetElement("a", Attributes = RouteValuesDictionaryName)]
    [HtmlTargetElement("a", Attributes = RouteValuesPrefix + "*")]
    public class CultureAnchorTagHelper : AnchorTagHelper
    {
        public CultureAnchorTagHelper(IHttpContextAccessor contextAccessor, IHtmlGenerator generator) :
            base(generator)
        {
            this.contextAccessor = contextAccessor;
        }

        private const string ActionAttributeName = "asp-action";
        private const string ControllerAttributeName = "asp-controller";
        private const string AreaAttributeName = "asp-area";
        private const string PageAttributeName = "asp-page";
        private const string PageHandlerAttributeName = "asp-page-handler";
        private const string FragmentAttributeName = "asp-fragment";
        private const string HostAttributeName = "asp-host";
        private const string ProtocolAttributeName = "asp-protocol";
        private const string RouteAttributeName = "asp-route";
        private const string RouteValuesDictionaryName = "asp-all-route-data";
        private const string RouteValuesPrefix = "asp-route-";
        private const string Href = "href";

        private readonly IHttpContextAccessor contextAccessor;
        private readonly string defaultRequestCulture = "en";

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            // Get the culture from the route values
            var culture = (string)contextAccessor.HttpContext.Request.RouteValues["culture"];
 
            // Set the culture in the route values if it is not null
            if (culture != null && culture != defaultRequestCulture)
            {
                // Remove the 'href' just in case
                output.Attributes.RemoveAll("href");
                RouteValues["culture"] = culture;
            }
           
            // Call the base class for all other functionality, we've only added the culture route value.
            // Because the route has the `{culture=en}`, the tag helper knows what to do with that route value.
            base.Process(context, output);
        }
    }
}

  1. 修改Shared/_ViewImports.cshtml删除当前的AnchorTagHelper,使用我们新创建的tag helper如下:
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, YourMvcName

应用所有更改后,将在您使用标签助手时填充区域性。