Reputation: 543
Migrating from .NET Core 3.1 to .NET 5, we encountered peculiar behavior with a catch-all route configuration.
The relevant part of the startup configuration looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "home",
defaults: new { controller = "Home", action = "Index" });
//catch-all endpoint
endpoints.Map("{*.}", async (t) =>
{
System.Console.WriteLine("hello");
await Task.CompletedTask;
});
});
}
Calling the url: http://localhost:port/home hits the catch-all route instead of the home controller. If the catch-all endpoint map is commented out, the home controller endpoint is hit - which is what we would expect in both cases, reading the MSDN docs. The bahavior before migration was the most specific route called first (i.e. the home controller endpoint), and the catch-all only responded when no route could be matched.
Were there breaking changes in .NET 5, or are we missing something?
Upvotes: 2
Views: 2040
Reputation: 543
The behavior was due to the ASP.NET Core team introducing a change to the routing mechanism in 5.0 - the problematic behavior is actually by design.
From javiercn on GitHub:
I looked more in depth and I can conclude that the behavior you are seeing is by design and caused by the fix we did in 5.0 to the routing bug we had in 3.1
Routes have order and precedence and order wins over precedence.
Conventional routes have an Order=1 (so that they are less specific than attribute routes and don't interfere with them when combined).
The second endpoint you are mapping doesn't have an order, so the order is 0.
The conventional route has more precedence than the catch-all route, but order is evaluated over the precedence.
That's why the route with the catch-all wins over the conventional route.
Conventional routes have a starting order of 1 and the order increases with each definition (to reflect the same behavior as classic mvc).
See more in the GitHub issue here: https://github.com/dotnet/aspnetcore/issues/29594
Upvotes: 1
Reputation: 5031
You can use priority to resolve the mismatch. This route can pick up any URL that has not been handled by any other route.
[Route("{*url}", Order = 999)]
public IActionResult CatchAll()
{
return View();
}
In this situation, another method is to use Status Code Pages.
In startup
app.UseExceptionHandler(option=>
{
app.UseStatusCodePagesWithReExecute("/error/{0}");
});
Controller
[Route("error/404")]
public IActionResult Error404()
{
return View();
}
Upvotes: 1