7

We are in the process of upgrading an ASP.NET Core 2.2 project to ASP.NET 3.0 that uses EndPoint routing.

We have a large list of urls constructed with Url.RouteUrl using named routes, e.g. :

string url = Url.RouteUrl("blog-details", new { title = item.Title, id = item.Id });
// returns correct link of https://example.org/us/blog/some-title-6 in 2.2 but is blank in 3.0

[Route("~/{lang}/blog/{title}-{id}", Name= "blog-details")]
public async Task<IActionResult> Details(string title, int id)
{
}

After upgrading to 3.0 those urls just produces a blank href. Our startup.cs is looking like this:

public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllersWithViews(options =>
{
    options.Filters.Add(new MiddlewareFilterAttribute(typeof(LocalizationPipeline))); 
})
    .AddViewLocalization(LanguageViewLocationExpanderFormat.SubFolder)
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);

services.AddRazorPages();
... 
}

We have tried replacing with below but that creates the wrong link and does not allow us to reuse as a variable, e.g.:

<a asp-action="Details" asp-controller="Blog" asp-route-title="item.Title" asp-route-id="@item.Id">Link here</a>
url = Url.Action("Details", "Blog", new { id = item.Id, title = item.Title });
url = Url.RouteUrl(new { action = "Details", controller = "Blog", id = item.Id, title = item.Title });

// all returns https://example.org/us/blog/details/6?title=some-title 

<a asp-controller="Home" asp-action="Pricing">Pricing</a>
// returns https://example.org/us/home/pricing instead of correct https://example.org/us/pricing

[Route("~/{lang}/pricing")]
public async Task<IActionResult> Pricing()
{
    ...
}

This works, however:

<a asp-controller="Signup" asp-action="Customer">Sign up</a>
// returns correct https://example.org/us/signup/customer

[Route("~/{lang}/signup/customer")]
public IActionResult Customer()
{
   ...
}

What are we doing wrong if we want to use EndPoint routing (not the old 2.2.-way)?

2
  • That seems the lang is null after migration to 3.0 app , check Globalization and localization in your 3.0 app. Commented Oct 2, 2019 at 7:22
  • @NanYu Hmm... could be but we added options.Filters.Add(new MiddlewareFilterAttribute(typeof(LocalizationPipeline))); to avoid decorating all controllers. Also added services.AddRouting(options => { options.ConstraintMap.Add("lang", typeof(LanguageRouteConstraint)); }); Commented Oct 2, 2019 at 11:31

1 Answer 1

8

There are two items that are responsible for this behavior.

First, let's look at how the routes are generated:

[Route("~/{lang}/pricing")]

the {lang} token is the problematic one. One would expect that when browsing from page to page - as it was the case under ASP.Net Core 2.2 - this value would be retained and re-used. It is not the case anymore. The doc justifies it using the id example: book id 123 should not lead to login user id 123. Unfortunately, the same applies to the much more stable language code lang.

The URL generation must therefore include the lang code. Using the 1st URL in the question, it would become:

string url = Url.RouteUrl("blog-details", new {lang="en", title = item.Title, id = item.Id });

[Route("~/{lang}/blog/{title}-{id}", Name= "blog-details")]

The second item is the default route. Since the route you though was not matched because of the missing lang parameter, the default route is used. Since you have used parameters that are not part of the default route template, they are added to the end of the URL (?title=some-title). If there is no default route, no URL at all is generated.

There is an interesting post on the issue that is worth reading.

On a side note for all readers, one can extract the ambient value that should be recycled and plug it in links generations:

@{string lang = (string)ViewContext.RouteData.Values["lang"]; }
<a asp-area="" asp-controller="myController" asp-action="myAction" asp-route-lang=@lang >click here</a>
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, very helpful! This answer deserves more visibility, it took me a while to find it
In my case missing token was at the controller level [Route("Api/v{version:apiVersion}/[controller]")] internal class XController.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.