Using RouteDataRequestCultureProvider in asp net core 3.1
Asked Answered
C

2

7

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?

Campney answered 10/12, 2019 at 13:8 Comment(2)
This is such a pain for me too, your solution below is helpful and probably the only way I can think of doing it.Faena
Same here. Did you find a way to generate an Url (like UrlHelper.Action) without specify the culture each time?Crosswalk
C
9

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:

Campney answered 11/12, 2019 at 10:15 Comment(0)
L
3

@herostwist, your solution saved my day :)

just a simplified version of it:

//...
public CultureAnchorTagHelper(IHtmlGenerator generator) : base(generator)
{
    RouteValues["culture"] = CultureInfo.CurrentCulture.Name;
}
Lenard answered 4/1, 2020 at 7:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.