Using RouteDataRequestCultureProvider in asp net c

2020-04-05 08:49发布

问题:

Having upgraded from core 2.2 to 3.1 I cannot get RouteDataRequestCultureProvider working fully.

Routing works but razor tag helpers are ignoring the culture and route attributes that use culture.

As simple example of custom route attribute:

public class StaticPageController : Controller
{
    [Route("{culture:culture}/cookies")]
    public IActionResult Cookies() => View();
}

If I go to the url https://localhost:5002/en/cookies the action is routed to correctly. However in razor, if I use a helper like this:

<a asp-controller="StaticPage" asp-action="Cookies">Cookie information</a>

The resulting link it generates is: https://localhost:5002/en/staticpage/cookies

In core 2.2 this was correctly generating the url https://localhost:5002/en/cookies

The route data dictionary correctly contains an entry for culture but the razor helper is no longer using it and is defaulting to the default route pattern:

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

Does anyone know why the razor tag helper is no longer using the culture from the route data dictionary?

EDIT:

I now know that culture is not being passed from the route values into the url generator because it is considered ambient, and ambient values are no longer included as of core 2.2.

Route Data values for the current request are considered ambient values in ASP.NET Core 2.1 and earlier

I can fix this by specifying the culture explicitly thus:

 <a 
    asp-controller="StaticPage" 
    asp-action="Cookies" asp-route- 
    culture="@Context.Request.RouteValues["culture"]">
       Cookies
 </a>

Specifying this for every link on every page is going to be very tedious.

Is there a way to override the tag helper to automatically include the current culture in the route values?

回答1:

So I ended up fixing this problem by overriding how the anchor tag helper works and making sure it always passes in the culture from the route.

This is the custom tag helper:

[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
{
    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-";


    public CultureAnchorTagHelper(IHtmlGenerator generator, IHttpContextAccessor contextAccessor) : base(generator)
    {
        var culture = contextAccessor.HttpContext.Request.RouteValues["culture"]?.ToString() ?? "en";
        RouteValues["culture"] = culture;
    }
}

To use it you need to remove the default anchor tag helper and add your own in _ViewImports.cshtml:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Your.Assembly.Namespace //assembly name containing class above

Some useful links:

  • anchor tag source code
  • tag helpers
  • extending tag helpers


回答2:

@herostwist, your solution saved my day :)

just a simplified version of it:

//...
public CultureAnchorTagHelper(IHtmlGenerator generator) : base(generator)
{
    RouteValues["culture"] = CultureInfo.CurrentCulture.Name;
}