diff --git a/aspnetcore/fundamentals/routing.md b/aspnetcore/fundamentals/routing.md index e4aa5e03e6..ba0a9d469e 100644 --- a/aspnetcore/fundamentals/routing.md +++ b/aspnetcore/fundamentals/routing.md @@ -343,7 +343,7 @@ The details of how precedence works are coupled to how route templates are defin * A segment with literal text is considered more specific than a parameter segment. * A parameter segment with a constraint is considered more specific than one without. * A complex segment is considered as specific as a parameter segment with a constraint. -* Catch all parameters are the least specific. +* Catch-all parameters are the least specific. See **catch-all** in the [Route template reference](#rtr) for important information on catch-all routes. See the [source code on GitHub](https://github.com/dotnet/aspnetcore/blob/master/src/Http/Routing/src/Template/RoutePrecedence.cs#L189) for a reference of exact values. @@ -411,6 +411,8 @@ Asterisk `*` or double asterisk `**`: * Matches any URI that starts with `/blog` and has any value following it. * The value following `/blog` is assigned to the [slug](https://developer.mozilla.org/docs/Glossary/Slug) route value. +[!INCLUDE[](~/includes/catchall.md)] + Catch-all parameters can also match the empty string. The catch-all parameter escapes the appropriate characters when the route is used to generate a URL, including path separator `/` characters. For example, the route `foo/{*path}` with route values `{ path = "my/path" }` generates `foo/my%2Fpath`. Note the escaped forward slash. To round-trip path separator characters, use the `**` route parameter prefix. The route `foo/{**path}` with `{ path = "my/path" }` generates `foo/my/path`. diff --git a/aspnetcore/includes/catchall.md b/aspnetcore/includes/catchall.md new file mode 100644 index 0000000000..a30bc5b129 --- /dev/null +++ b/aspnetcore/includes/catchall.md @@ -0,0 +1,19 @@ +> [!WARNING] +> A **catch-all** parameter may match routes incorrectly due to a [bug](https://github.com/dotnet/aspnetcore/issues/18677) in routing. Apps impacted by this bug have the following characteristics: +> +> * A catch-all route, for example, `{**slug}"` +> * The catch-all route fails to match requests it should match. +> * Removing other routes makes catch-all route start working. +> +> See GitHub bugs [18677](https://github.com/dotnet/aspnetcore/issues/18677) and [16579](https://github.com/dotnet/aspnetcore/issues/16579) for example cases that hit this bug. +> +> An opt-in fix for this bug is planned. This doc will be updated when the patch is released. When the patch is released, the following code will set an internal switch that fixes this bug: +> +>``` +>public static void Main(string[] args) +>{ +> AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", true); +> CreateHostBuilder(args).Build().Run(); +>} +>// Remaining code removed for brevity. +>``` \ No newline at end of file diff --git a/aspnetcore/migration/22-to-30.md b/aspnetcore/migration/22-to-30.md index ecd8302f67..fd3696aab8 100644 --- a/aspnetcore/migration/22-to-30.md +++ b/aspnetcore/migration/22-to-30.md @@ -1217,6 +1217,10 @@ Review breaking changes: * [Breaking API changes in Antiforgery, CORS, Diagnostics, MVC, and Routing](https://github.com/aspnet/Announcements/issues/387). This list includes breaking changes for compatibility switches. * For a summary of 2.2-to-3.0 breaking changes across .NET Core, ASP.NET Core, and Entity Framework Core, see [Breaking changes for migration from version 2.2 to 3.0](/dotnet/core/compatibility/2.2-3.0). +## Endpoint routing with catch-all parameter + +[!INCLUDE[](~/includes/catchall.md)] + ## .NET Core 3.0 on Azure App Service The rollout of .NET Core to Azure App Service is finished. .NET Core 3.0 is available in all Azure App Service datacenters. diff --git a/aspnetcore/mvc/controllers/routing.md b/aspnetcore/mvc/controllers/routing.md index 89479cf0da..6b430b7f3d 100644 --- a/aspnetcore/mvc/controllers/routing.md +++ b/aspnetcore/mvc/controllers/routing.md @@ -190,7 +190,9 @@ The preceding example: ### Conventional routing order Conventional routing only matches a combination of action and controller that are defined by the app. This is intended to simplify cases where conventional routes overlap. -Adding routes using , , and automatically assign an order value to their endpoints based on the order they are invoked. Matches from a route that appears earlier have a higher priority. Conventional routing is order-dependent. In general, routes with areas should be placed earlier as they're more specific than routes without an area. [Dedicated conventional routes](#dcr) with catch all route parameters like `{*article}` can make a route too [greedy](xref:fundamentals/routing#greedy), meaning that it matches URLs that you intended to be matched by other routes. Put the greedy routes later in the route table to prevent greedy matches. +Adding routes using , , and automatically assign an order value to their endpoints based on the order they are invoked. Matches from a route that appears earlier have a higher priority. Conventional routing is order-dependent. In general, routes with areas should be placed earlier as they're more specific than routes without an area. [Dedicated conventional routes](#dcr) with catch-all route parameters like `{*article}` can make a route too [greedy](xref:fundamentals/routing#greedy), meaning that it matches URLs that you intended to be matched by other routes. Put the greedy routes later in the route table to prevent greedy matches. + +[!INCLUDE[](~/includes/catchall.md)]