From a7d32fd269a8a003c78c173c4df1528c7a1c405d Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 30 Jan 2023 14:57:58 +1100 Subject: [PATCH 01/37] Implement AddRoutingCore to support excluding Regex. --- .../src/WebApplicationBuilder.cs | 10 ++++++++ src/DefaultBuilder/src/WebHost.cs | 2 +- .../RoutingServiceCollectionExtensions.cs | 25 ++++++++++++++++--- src/Http/Routing/src/RouteOptions.cs | 17 +++++++++---- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 0f7bca59694c..ed581871bbcc 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -59,6 +59,11 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action { + webHostBuilder.ConfigureServices(services => + { + services.AddRouting(); + }); + // Runs inline. webHostBuilder.Configure(ConfigureApplication); @@ -136,6 +141,11 @@ internal WebApplicationBuilder(WebApplicationOptions options, bool slim, Action< { AspNetCore.WebHost.UseKestrel(webHostBuilder); + webHostBuilder.ConfigureServices(services => + { + services.AddRoutingCore(); + }); + webHostBuilder.Configure(ConfigureEmptyApplication); webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? ""); diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 4a49c3ef8e3c..4a2f95b0cddb 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -257,7 +257,7 @@ internal static void UseKestrel(IWebHostBuilder builder) services.AddTransient(); services.AddTransient, ForwardedHeadersOptionsSetup>(); - services.AddRouting(); + services.AddRoutingCore(); }); } diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 6c912748bca5..d120c746b7e5 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -20,12 +20,20 @@ namespace Microsoft.Extensions.DependencyInjection; /// public static class RoutingServiceCollectionExtensions { + public static IServiceCollection AddRouting(this IServiceCollection services) + { + return services.AddRoutingCore(routeOptions => + { + routeOptions.AddRegexConstraints(); + }); + } + /// /// Adds services required for routing requests. /// /// The to add the services to. /// The so that additional calls can be chained. - public static IServiceCollection AddRouting(this IServiceCollection services) + public static IServiceCollection AddRoutingCore(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); @@ -101,13 +109,24 @@ public static IServiceCollection AddRouting(this IServiceCollection services) return services; } + public static IServiceCollection AddRouting( + this IServiceCollection services, + Action configureOptions) + { + return services.AddRoutingCore(routeOptions => + { + routeOptions.AddRegexConstraints(); + configureOptions(routeOptions); + }); + } + /// /// Adds services required for routing requests. /// /// The to add the services to. /// The routing options to configure the middleware with. /// The so that additional calls can be chained. - public static IServiceCollection AddRouting( + public static IServiceCollection AddRoutingCore( this IServiceCollection services, Action configureOptions) { @@ -115,7 +134,7 @@ public static IServiceCollection AddRouting( ArgumentNullException.ThrowIfNull(configureOptions); services.Configure(configureOptions); - services.AddRouting(); + services.AddRoutingCore(); return services; } diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 4ae9a9688bf8..2dbac8df94e9 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Routing.Constraints; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Routing; @@ -89,6 +90,16 @@ public IDictionary ConstraintMap /// internal IDictionary TrimmerSafeConstraintMap => _constraintTypeMap; + /// + /// Add Regex-based constraints to the constraint map (e.g. alpha and regex constraint types). This is called automatically by . + /// + public void AddRegexConstraints() + { + // Regex-based constraints + AddConstraint(_constraintTypeMap, "alpha"); + AddConstraint(_constraintTypeMap, "regex"); + } + private static IDictionary GetDefaultConstraintMap() { var defaults = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -113,10 +124,6 @@ private static IDictionary GetDefaultConstraintMap() AddConstraint(defaults, "max"); AddConstraint(defaults, "range"); - // Regex-based constraints - AddConstraint(defaults, "alpha"); - AddConstraint(defaults, "regex"); - AddConstraint(defaults, "required"); // Files @@ -152,7 +159,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic _constraintTypeMap[token] = type; } - private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TConstraint>(Dictionary constraintMap, string text) where TConstraint : IRouteConstraint + private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TConstraint>(IDictionary constraintMap, string text) where TConstraint : IRouteConstraint { constraintMap[text] = typeof(TConstraint); } From 473587bd813b70ce3d73eccac00257d2e0eb40ec Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 30 Jan 2023 17:37:53 +1100 Subject: [PATCH 02/37] Rename UseKestrel. --- src/DefaultBuilder/src/WebApplicationBuilder.cs | 7 +------ src/DefaultBuilder/src/WebHost.cs | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index ed581871bbcc..5638462177c4 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -139,12 +139,7 @@ internal WebApplicationBuilder(WebApplicationOptions options, bool slim, Action< bootstrapHostBuilder.ConfigureSlimWebHost( webHostBuilder => { - AspNetCore.WebHost.UseKestrel(webHostBuilder); - - webHostBuilder.ConfigureServices(services => - { - services.AddRoutingCore(); - }); + AspNetCore.WebHost.ConfigureWebDefaultsCore(webHostBuilder); webHostBuilder.Configure(ConfigureEmptyApplication); diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 4a2f95b0cddb..cd2eadb5b802 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -223,14 +223,14 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder) } }); - UseKestrel(builder); + ConfigureWebDefaultsCore(builder); builder .UseIIS() .UseIISIntegration(); } - internal static void UseKestrel(IWebHostBuilder builder) + internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder) { builder.UseKestrel((builderContext, options) => { From 63661ce9ce382f260ebbaee7c2b56654b40b0086 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Tue, 31 Jan 2023 12:21:26 +1100 Subject: [PATCH 03/37] Add XML docs and API. --- .../RoutingServiceCollectionExtensions.cs | 21 +++++++++++++++++-- src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index d120c746b7e5..3d8328dcb16c 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -20,6 +20,11 @@ namespace Microsoft.Extensions.DependencyInjection; /// public static class RoutingServiceCollectionExtensions { + /// + /// Adds services required for routing requests. + /// + /// The to add the services to. + /// The so that additional calls can be chained. public static IServiceCollection AddRouting(this IServiceCollection services) { return services.AddRoutingCore(routeOptions => @@ -29,7 +34,10 @@ public static IServiceCollection AddRouting(this IServiceCollection services) } /// - /// Adds services required for routing requests. + /// Adds services required for routing requests. This is similar to + /// except that it + /// excludes certain options that are not included by default for + /// native AOT scenarios. /// /// The to add the services to. /// The so that additional calls can be chained. @@ -109,6 +117,12 @@ public static IServiceCollection AddRoutingCore(this IServiceCollection services return services; } + /// + /// Adds services required for routing requests. + /// + /// The to add the services to. + /// The routing options to configure the middleware with. + /// The so that additional calls can be chained. public static IServiceCollection AddRouting( this IServiceCollection services, Action configureOptions) @@ -121,7 +135,10 @@ public static IServiceCollection AddRouting( } /// - /// Adds services required for routing requests. + /// Adds services required for routing requests. This is similar to + /// + /// except that it excludes certain options that are not included by default for native AOT + /// scenarios. /// /// The to add the services to. /// The routing options to configure the middleware with. diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..462cd190e7f9 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ #nullable enable +static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! From e9ac4e75e0b85ed8a0edf8082f4ece753b38d2f4 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Wed, 1 Feb 2023 17:55:21 +1100 Subject: [PATCH 04/37] Added test cases to cover removing regex. --- .../WebApplicationTests.cs | 137 ++++++++++++++++++ .../RoutingServiceCollectionExtensions.cs | 12 +- src/Http/Routing/src/PublicAPI.Unshipped.txt | 1 + src/Http/Routing/src/RouteOptions.cs | 5 +- 4 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 104e13788f2c..834e31da2370 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -2267,6 +2267,143 @@ public async Task SupportsDisablingMiddlewareAutoRegistration() Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); } + [Fact] + public async Task UsingCreateBuilderResultsInRegexConstraintBeingPresent() + { + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseTestServer(); + + var app = builder.Build(); + + var chosenRoute = string.Empty; + + app.Use((context, next) => + { + chosenRoute = context.GetEndpoint()?.DisplayName; + return next(context); + }); + + app.MapGet("/products/{productId:regex(^[a-z]{{4}}\\d{{4}}$)}", (string productId) => productId).WithDisplayName("RegexRoute"); + + await app.StartAsync(); + + var client = app.GetTestClient(); + + _ = await client.GetAsync("https://localhost/products/abcd1234"); + Assert.Equal("RegexRoute", chosenRoute); + } + + [Fact] + public async Task UsingCreateSlimBuilderResultsInAlphaConstraintStillWorking() + { + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseTestServer(); + + var app = builder.Build(); + + var chosenRoute = string.Empty; + + app.Use((context, next) => + { + chosenRoute = context.GetEndpoint()?.DisplayName; + return next(context); + }); + + app.MapGet("/products/{productId:alpha:minlength(4):maxlength(4)}", (string productId) => productId).WithDisplayName("AlphaRoute"); + + await app.StartAsync(); + + var client = app.GetTestClient(); + + _ = await client.GetAsync("https://localhost/products/abcd"); + Assert.Equal("AlphaRoute", chosenRoute); + } + + [Fact] + public async Task UsingCreateSlimBuilderResultsInErrorWhenTryingToUseRegexConstraint() + { + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseTestServer(); + + var app = builder.Build(); + + var chosenRoute = string.Empty; + + app.Use((context, next) => + { + chosenRoute = context.GetEndpoint()?.DisplayName; + return next(context); + }); + + app.MapGet("/products/{productId:regex(^[a-z]{{4}}\\d{{4}}$)}", (string productId) => productId).WithDisplayName("AlphaRoute"); + + await app.StartAsync(); + + var client = app.GetTestClient(); + + await Assert.ThrowsAsync(async () => + { + _ = await client.GetAsync("https://localhost/products/abcd1234"); + }); + } + + [Fact] + public async Task UsingCreateSlimBuilderWorksIfRegexConstraintAddedViaAddRouting() + { + var builder = WebApplication.CreateSlimBuilder(); + builder.Services.AddRouting(); + builder.WebHost.UseTestServer(); + + var app = builder.Build(); + + var chosenRoute = string.Empty; + + app.Use((context, next) => + { + chosenRoute = context.GetEndpoint()?.DisplayName; + return next(context); + }); + + app.MapGet("/products/{productId:regex(^[a-z]{{4}}\\d{{4}}$)}", (string productId) => productId).WithDisplayName("RegexRoute"); + + await app.StartAsync(); + + var client = app.GetTestClient(); + + _ = await client.GetAsync("https://localhost/products/abcd1234"); + Assert.Equal("RegexRoute", chosenRoute); + } + + [Fact] + public async Task UsingCreateSlimBuilderWorksIfRegexConstraintAddedViaAddRoutingCoreWithActionDelegate() + { + var builder = WebApplication.CreateSlimBuilder(); + builder.Services.AddRoutingCore(options => + { + options.AddRegexConstraint(); + }); + builder.WebHost.UseTestServer(); + + var app = builder.Build(); + + var chosenRoute = string.Empty; + + app.Use((context, next) => + { + chosenRoute = context.GetEndpoint()?.DisplayName; + return next(context); + }); + + app.MapGet("/products/{productId:regex(^[a-z]{{4}}\\d{{4}}$)}", (string productId) => productId).WithDisplayName("RegexRoute"); + + await app.StartAsync(); + + var client = app.GetTestClient(); + + _ = await client.GetAsync("https://localhost/products/abcd1234"); + Assert.Equal("RegexRoute", chosenRoute); + } + private class UberHandler : AuthenticationHandler { public UberHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 3d8328dcb16c..6b2ab1ec5e0a 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -29,15 +29,14 @@ public static IServiceCollection AddRouting(this IServiceCollection services) { return services.AddRoutingCore(routeOptions => { - routeOptions.AddRegexConstraints(); + routeOptions.AddRegexConstraint(); }); } /// /// Adds services required for routing requests. This is similar to - /// except that it - /// excludes certain options that are not included by default for - /// native AOT scenarios. + /// except that it + /// excludes certain options that can be opted in separately, if needed. /// /// The to add the services to. /// The so that additional calls can be chained. @@ -129,7 +128,7 @@ public static IServiceCollection AddRouting( { return services.AddRoutingCore(routeOptions => { - routeOptions.AddRegexConstraints(); + routeOptions.AddRegexConstraint(); configureOptions(routeOptions); }); } @@ -137,8 +136,7 @@ public static IServiceCollection AddRouting( /// /// Adds services required for routing requests. This is similar to /// - /// except that it excludes certain options that are not included by default for native AOT - /// scenarios. + /// except that it excludes certain options that can be opted in separately, if needed. /// /// The to add the services to. /// The routing options to configure the middleware with. diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 2a2d210cd81e..2d2e4c393d25 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +Microsoft.AspNetCore.Routing.RouteOptions.AddRegexConstraint() -> void static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! Microsoft.AspNetCore.Routing.RouteHandlerServices diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 2dbac8df94e9..f9394a477c61 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -93,10 +93,9 @@ public IDictionary ConstraintMap /// /// Add Regex-based constraints to the constraint map (e.g. alpha and regex constraint types). This is called automatically by . /// - public void AddRegexConstraints() + public void AddRegexConstraint() { // Regex-based constraints - AddConstraint(_constraintTypeMap, "alpha"); AddConstraint(_constraintTypeMap, "regex"); } @@ -124,6 +123,8 @@ private static IDictionary GetDefaultConstraintMap() AddConstraint(defaults, "max"); AddConstraint(defaults, "range"); + AddConstraint(defaults, "alpha"); + AddConstraint(defaults, "required"); // Files From 1d723ee546a4a1855f6d49027915efc7f1058ec3 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Wed, 1 Feb 2023 19:58:27 +1100 Subject: [PATCH 05/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: James Newton-King --- src/Http/Routing/src/RouteOptions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index f9394a477c61..5f72eb1fa3e9 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -123,6 +123,7 @@ private static IDictionary GetDefaultConstraintMap() AddConstraint(defaults, "max"); AddConstraint(defaults, "range"); + // The alpha constraint uses a compiled regex which has a minimal size cost. AddConstraint(defaults, "alpha"); AddConstraint(defaults, "required"); From 008f937d8de65173bb687f273ba40c033d9ac3b6 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Thu, 2 Feb 2023 09:10:13 +1100 Subject: [PATCH 06/37] Cleaned up some tests and added message assertion. --- .../WebApplicationTests.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 834e31da2370..514ed36ba2db 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -2327,24 +2327,21 @@ public async Task UsingCreateSlimBuilderResultsInErrorWhenTryingToUseRegexConstr var app = builder.Build(); - var chosenRoute = string.Empty; - - app.Use((context, next) => - { - chosenRoute = context.GetEndpoint()?.DisplayName; - return next(context); - }); - app.MapGet("/products/{productId:regex(^[a-z]{{4}}\\d{{4}}$)}", (string productId) => productId).WithDisplayName("AlphaRoute"); await app.StartAsync(); var client = app.GetTestClient(); - await Assert.ThrowsAsync(async () => + var ex = await Record.ExceptionAsync(async () => { _ = await client.GetAsync("https://localhost/products/abcd1234"); }); + + Assert.IsType(ex); + Assert.Equal( + "The constraint reference 'regex' could not be resolved to a type. Register the constraint type with 'Microsoft.AspNetCore.Routing.RouteOptions.ConstraintMap'.", + ex.Message); } [Fact] From fc44ea305930c41ab54b274ef5ace85a19d929ad Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 3 Feb 2023 12:40:08 +1100 Subject: [PATCH 07/37] Remove AddRegexConstraint and use SetParameterPolicy instead. --- .../WebApplicationTests.cs | 5 +-- .../RoutingServiceCollectionExtensions.cs | 32 ++++--------------- src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 -- src/Http/Routing/src/RouteOptions.cs | 9 ------ .../DefaultInlineConstraintResolverTest.cs | 2 ++ .../Matching/DfaMatcherBuilderTest.cs | 3 +- .../UnitTests/Matching/RouteMatcherBuilder.cs | 5 ++- .../Matching/TreeRouterMatcherBuilder.cs | 6 +++- src/Http/Routing/test/UnitTests/RouteTest.cs | 1 + .../TemplateParserDefaultValuesTests.cs | 5 ++- .../test/UnitTests/Tree/TreeRouterTest.cs | 3 ++ 11 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 514ed36ba2db..96b07dffe62f 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Tests; @@ -2375,9 +2376,9 @@ public async Task UsingCreateSlimBuilderWorksIfRegexConstraintAddedViaAddRouting public async Task UsingCreateSlimBuilderWorksIfRegexConstraintAddedViaAddRoutingCoreWithActionDelegate() { var builder = WebApplication.CreateSlimBuilder(); - builder.Services.AddRoutingCore(options => + builder.Services.AddRoutingCore().Configure(options => { - options.AddRegexConstraint(); + options.SetParameterPolicy("regex"); }); builder.WebHost.UseTestServer(); diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 6b2ab1ec5e0a..b81f3e480e1b 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Patterns; @@ -27,9 +28,9 @@ public static class RoutingServiceCollectionExtensions /// The so that additional calls can be chained. public static IServiceCollection AddRouting(this IServiceCollection services) { - return services.AddRoutingCore(routeOptions => + return services.AddRoutingCore().Configure(options => { - routeOptions.AddRegexConstraint(); + options.SetParameterPolicy("regex"); }); } @@ -126,31 +127,10 @@ public static IServiceCollection AddRouting( this IServiceCollection services, Action configureOptions) { - return services.AddRoutingCore(routeOptions => + return services.AddRoutingCore().Configure(options => { - routeOptions.AddRegexConstraint(); - configureOptions(routeOptions); + options.SetParameterPolicy("regex"); + configureOptions(options); }); } - - /// - /// Adds services required for routing requests. This is similar to - /// - /// except that it excludes certain options that can be opted in separately, if needed. - /// - /// The to add the services to. - /// The routing options to configure the middleware with. - /// The so that additional calls can be chained. - public static IServiceCollection AddRoutingCore( - this IServiceCollection services, - Action configureOptions) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(configureOptions); - - services.Configure(configureOptions); - services.AddRoutingCore(); - - return services; - } } diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 2d2e4c393d25..277dc63d5f83 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -1,6 +1,4 @@ #nullable enable -Microsoft.AspNetCore.Routing.RouteOptions.AddRegexConstraint() -> void static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! Microsoft.AspNetCore.Routing.RouteHandlerServices static Microsoft.AspNetCore.Routing.RouteHandlerServices.Map(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler, System.Collections.Generic.IEnumerable! httpMethods, System.Func! populateMetadata, System.Func! createRequestDelegate) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 5f72eb1fa3e9..6f8cc15bdb85 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -90,15 +90,6 @@ public IDictionary ConstraintMap /// internal IDictionary TrimmerSafeConstraintMap => _constraintTypeMap; - /// - /// Add Regex-based constraints to the constraint map (e.g. alpha and regex constraint types). This is called automatically by . - /// - public void AddRegexConstraint() - { - // Regex-based constraints - AddConstraint(_constraintTypeMap, "regex"); - } - private static IDictionary GetDefaultConstraintMap() { var defaults = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/src/Http/Routing/test/UnitTests/DefaultInlineConstraintResolverTest.cs b/src/Http/Routing/test/UnitTests/DefaultInlineConstraintResolverTest.cs index 31fd8f7387f5..be7bc7e3524a 100644 --- a/src/Http/Routing/test/UnitTests/DefaultInlineConstraintResolverTest.cs +++ b/src/Http/Routing/test/UnitTests/DefaultInlineConstraintResolverTest.cs @@ -16,6 +16,8 @@ public class DefaultInlineConstraintResolverTest public DefaultInlineConstraintResolverTest() { var routeOptions = new RouteOptions(); + routeOptions.SetParameterPolicy("regex"); + _constraintResolver = GetInlineConstraintResolver(routeOptions); } diff --git a/src/Http/Routing/test/UnitTests/Matching/DfaMatcherBuilderTest.cs b/src/Http/Routing/test/UnitTests/Matching/DfaMatcherBuilderTest.cs index cfc4acb837b6..47d67f6eb601 100644 --- a/src/Http/Routing/test/UnitTests/Matching/DfaMatcherBuilderTest.cs +++ b/src/Http/Routing/test/UnitTests/Matching/DfaMatcherBuilderTest.cs @@ -3546,7 +3546,8 @@ private static DefaultParameterPolicyFactory CreateParameterPolicyFactory() ConstraintMap = { ["slugify"] = typeof(SlugifyParameterTransformer), - ["upper-case"] = typeof(UpperCaseParameterTransform) + ["upper-case"] = typeof(UpperCaseParameterTransform), + ["regex"] = typeof(RegexInlineRouteConstraint) // Regex not included by default since introduction of CreateSlimBuilder } }), serviceCollection.BuildServiceProvider()); diff --git a/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs b/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs index 5cc7fed190db..57496ffa6dd7 100644 --- a/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs +++ b/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.Extensions.Options; @@ -15,7 +16,9 @@ internal class RouteMatcherBuilder : MatcherBuilder public RouteMatcherBuilder() { - _constraintResolver = new DefaultInlineConstraintResolver(Options.Create(new RouteOptions()), new TestServiceProvider()); + var routeOptions = new RouteOptions(); + routeOptions.SetParameterPolicy("regex"); + _constraintResolver = new DefaultInlineConstraintResolver(Options.Create(routeOptions), new TestServiceProvider()); _endpoints = new List(); } diff --git a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs index 51ffe2e7c0a3..9617f4e815f2 100644 --- a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs +++ b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Routing.Tree; @@ -27,10 +28,13 @@ public override void AddEndpoint(RouteEndpoint endpoint) public override Matcher Build() { + var routeOptions = new RouteOptions(); + routeOptions.SetParameterPolicy("regex"); + var builder = new TreeRouteBuilder( NullLoggerFactory.Instance, new DefaultObjectPool(new UriBuilderContextPooledObjectPolicy()), - new DefaultInlineConstraintResolver(Options.Create(new RouteOptions()), new TestServiceProvider())); + new DefaultInlineConstraintResolver(Options.Create(routeOptions), new TestServiceProvider())); var selector = new DefaultEndpointSelector(); diff --git a/src/Http/Routing/test/UnitTests/RouteTest.cs b/src/Http/Routing/test/UnitTests/RouteTest.cs index 3042f805ca1d..767e02a737c9 100644 --- a/src/Http/Routing/test/UnitTests/RouteTest.cs +++ b/src/Http/Routing/test/UnitTests/RouteTest.cs @@ -1861,6 +1861,7 @@ private static IInlineConstraintResolver GetInlineConstraintResolver() private static void ConfigureRouteOptions(RouteOptions options) { options.ConstraintMap["test-policy"] = typeof(TestPolicy); + options.SetParameterPolicy("regex"); } private class TestPolicy : IParameterPolicy diff --git a/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs b/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs index 8a98737930c2..463c1b62dfc0 100644 --- a/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs +++ b/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -128,7 +129,9 @@ private static IRouteBuilder CreateRouteBuilder() services.AddSingleton(_inlineConstraintResolver); services.AddSingleton(); services.AddSingleton(); - services.Configure(options => { }); + services.Configure(options => { + options.SetParameterPolicy("regex"); + }); var applicationBuilder = Mock.Of(); applicationBuilder.ApplicationServices = services.BuildServiceProvider(); diff --git a/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs b/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs index 3658dbd5a63b..951ebc1fd4c5 100644 --- a/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs +++ b/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs @@ -3,6 +3,7 @@ using System.Globalization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.Extensions.Logging; @@ -2056,6 +2057,8 @@ private static string CreateRouteGroup(int order, string template) private static DefaultInlineConstraintResolver CreateConstraintResolver() { var options = new RouteOptions(); + options.SetParameterPolicy("regex"); + var optionsMock = new Mock>(); optionsMock.SetupGet(o => o.Value).Returns(options); From 78056ab5db2f7ad24a3c3b61fe15fb49496cb8ea Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 3 Feb 2023 13:29:08 +1100 Subject: [PATCH 08/37] Introduce dummy route constraint to give helpful error message. --- .../WebApplicationTests.cs | 2 +- src/Http/Routing/src/ParameterPolicyActivator.cs | 2 +- src/Http/Routing/src/Resources.resx | 3 +++ src/Http/Routing/src/RouteOptions.cs | 16 ++++++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 96b07dffe62f..62a20fa2ffb4 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -2341,7 +2341,7 @@ public async Task UsingCreateSlimBuilderResultsInErrorWhenTryingToUseRegexConstr Assert.IsType(ex); Assert.Equal( - "The constraint reference 'regex' could not be resolved to a type. Register the constraint type with 'Microsoft.AspNetCore.Routing.RouteOptions.ConstraintMap'.", + "The route parameter 'productId' uses a regular expression constraint. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) it may not include the RegexInlineRouteConstraint. To register this route route constraint call SetParameterPolicy(Action configureOptions) extension method.", ex.Message); } diff --git a/src/Http/Routing/src/ParameterPolicyActivator.cs b/src/Http/Routing/src/ParameterPolicyActivator.cs index 37e567d93cec..fe616822976b 100644 --- a/src/Http/Routing/src/ParameterPolicyActivator.cs +++ b/src/Http/Routing/src/ParameterPolicyActivator.cs @@ -95,7 +95,7 @@ private static IParameterPolicy CreateParameterPolicy(IServiceProvider? serviceP { ConstructorInfo? activationConstructor = null; object?[]? parameters = null; - var constructors = parameterPolicyType.GetConstructors(); + var constructors = parameterPolicyType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // If there is only one constructor and it has a single parameter, pass the argument string directly // This is necessary for the Regex RouteConstraint to ensure that patterns are not split on commas. diff --git a/src/Http/Routing/src/Resources.resx b/src/Http/Routing/src/Resources.resx index 9b3d90ce5b04..0ce2c241ee8e 100644 --- a/src/Http/Routing/src/Resources.resx +++ b/src/Http/Routing/src/Resources.resx @@ -249,4 +249,7 @@ This RequestDelegate cannot be called before the final endpoint is built. + + The route parameter '{0}' uses a regular expression constraint. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) it may not include the RegexInlineRouteConstraint. To register this route route constraint call SetParameterPolicy<RegexInlineRouteConstraint("regex") on RouteOptions via the Configure<TOptions>(Action<RouteOptions> configureOptions) extension method. + \ No newline at end of file diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 6f8cc15bdb85..53b86bc7ea2f 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.Extensions.DependencyInjection; @@ -117,6 +119,8 @@ private static IDictionary GetDefaultConstraintMap() // The alpha constraint uses a compiled regex which has a minimal size cost. AddConstraint(defaults, "alpha"); + AddConstraint(defaults, "regex"); // Used to generate error message at runtime with helpful message. + AddConstraint(defaults, "required"); // Files @@ -157,3 +161,15 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic constraintMap[text] = typeof(TConstraint); } } + +internal class RegexErrorStubRouteConstraint : IRouteConstraint +{ + internal RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string regexPattern) + { + } + + bool IRouteConstraint.Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) + { + throw new InvalidOperationException(Resources.FormatRegexRouteContraint_NotConfigured(routeKey)); + } +} From ddf8fd9bdb43d55ce53b390d6566f419ed59b41d Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 3 Feb 2023 20:17:45 +1100 Subject: [PATCH 09/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: James Newton-King --- src/Http/Routing/src/RouteOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 53b86bc7ea2f..3b1c34864870 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -162,7 +162,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic } } -internal class RegexErrorStubRouteConstraint : IRouteConstraint +internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint { internal RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string regexPattern) { From 6c9d3d200d864a76ae93f2a4a54a893f53f41dd8 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 3 Feb 2023 20:18:01 +1100 Subject: [PATCH 10/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: James Newton-King --- src/Http/Routing/src/RouteOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 3b1c34864870..2812e09e2069 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -164,7 +164,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint { - internal RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string regexPattern) + public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string regexPattern) { } From 30703200eda27916442a637d290822184a659d4e Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 08:59:52 +1100 Subject: [PATCH 11/37] Update src/Http/Routing/src/ParameterPolicyActivator.cs Co-authored-by: James Newton-King --- src/Http/Routing/src/ParameterPolicyActivator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/ParameterPolicyActivator.cs b/src/Http/Routing/src/ParameterPolicyActivator.cs index fe616822976b..37e567d93cec 100644 --- a/src/Http/Routing/src/ParameterPolicyActivator.cs +++ b/src/Http/Routing/src/ParameterPolicyActivator.cs @@ -95,7 +95,7 @@ private static IParameterPolicy CreateParameterPolicy(IServiceProvider? serviceP { ConstructorInfo? activationConstructor = null; object?[]? parameters = null; - var constructors = parameterPolicyType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var constructors = parameterPolicyType.GetConstructors(); // If there is only one constructor and it has a single parameter, pass the argument string directly // This is necessary for the Regex RouteConstraint to ensure that patterns are not split on commas. From e6d632f532accc4d50695111db164b7bade67a87 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 09:00:03 +1100 Subject: [PATCH 12/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: James Newton-King --- src/Http/Routing/src/RouteOptions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 2812e09e2069..73617320969e 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -6,7 +6,6 @@ using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Constraints; -using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Routing; From 03bfcd9db34b11c2df06f4e1b1952002ae122b0f Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 09:00:48 +1100 Subject: [PATCH 13/37] Update src/Http/Routing/src/Resources.resx Co-authored-by: James Newton-King --- src/Http/Routing/src/Resources.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/Resources.resx b/src/Http/Routing/src/Resources.resx index 0ce2c241ee8e..f5349f6bc49c 100644 --- a/src/Http/Routing/src/Resources.resx +++ b/src/Http/Routing/src/Resources.resx @@ -250,6 +250,6 @@ This RequestDelegate cannot be called before the final endpoint is built. - The route parameter '{0}' uses a regular expression constraint. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) it may not include the RegexInlineRouteConstraint. To register this route route constraint call SetParameterPolicy<RegexInlineRouteConstraint("regex") on RouteOptions via the Configure<TOptions>(Action<RouteOptions> configureOptions) extension method. + Route parameter '{0}' uses the regex constraint, which isn't registered. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) then this constraint is not registered by default. To use the regex constraint, configure route options at app startup: services.Configure<RouteOptions>(options => options.SetParameterPolicy<RegexInlineRouteConstraint>("regex")); \ No newline at end of file From 1c94ae42fa253e5db06133ee4486b7750a344820 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 09:28:45 +1100 Subject: [PATCH 14/37] Throw on first request. --- .../test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs | 7 ++++--- src/Http/Routing/src/Resources.resx | 2 +- src/Http/Routing/src/RouteOptions.cs | 4 +++- .../test/UnitTests/TemplateParserDefaultValuesTests.cs | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 62a20fa2ffb4..b583cd4b4482 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -2339,10 +2339,11 @@ public async Task UsingCreateSlimBuilderResultsInErrorWhenTryingToUseRegexConstr _ = await client.GetAsync("https://localhost/products/abcd1234"); }); - Assert.IsType(ex); + Assert.IsType(ex); + Assert.IsType(ex.InnerException.InnerException); Assert.Equal( - "The route parameter 'productId' uses a regular expression constraint. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) it may not include the RegexInlineRouteConstraint. To register this route route constraint call SetParameterPolicy(Action configureOptions) extension method.", - ex.Message); + "A route parameter uses the regex constraint, which isn't registered. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) then this constraint is not registered by default. To use the regex constraint, configure route options at app startup: services.Configure(options => options.SetParameterPolicy(\"regex\"));", + ex.InnerException.InnerException.Message); } [Fact] diff --git a/src/Http/Routing/src/Resources.resx b/src/Http/Routing/src/Resources.resx index f5349f6bc49c..a1fe756e745d 100644 --- a/src/Http/Routing/src/Resources.resx +++ b/src/Http/Routing/src/Resources.resx @@ -250,6 +250,6 @@ This RequestDelegate cannot be called before the final endpoint is built. - Route parameter '{0}' uses the regex constraint, which isn't registered. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) then this constraint is not registered by default. To use the regex constraint, configure route options at app startup: services.Configure<RouteOptions>(options => options.SetParameterPolicy<RegexInlineRouteConstraint>("regex")); + A route parameter uses the regex constraint, which isn't registered. If this application was configured using CreateSlimBuilder(...) or AddRoutingCore(...) then this constraint is not registered by default. To use the regex constraint, configure route options at app startup: services.Configure<RouteOptions>(options => options.SetParameterPolicy<RegexInlineRouteConstraint>("regex")); \ No newline at end of file diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 73617320969e..66ef1888858f 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -165,10 +165,12 @@ internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint { public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string regexPattern) { + throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); } bool IRouteConstraint.Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { - throw new InvalidOperationException(Resources.FormatRegexRouteContraint_NotConfigured(routeKey)); + // Should never get called, but is same as throw in constructor in case constructor is changed. + throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); } } diff --git a/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs b/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs index 463c1b62dfc0..864bb35a7e4a 100644 --- a/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs +++ b/src/Http/Routing/test/UnitTests/TemplateParserDefaultValuesTests.cs @@ -146,6 +146,8 @@ private static IInlineConstraintResolver GetInlineConstraintResolver() var services = new ServiceCollection().AddOptions(); var serviceProvider = services.BuildServiceProvider(); var accessor = serviceProvider.GetRequiredService>(); + accessor.Value.SetParameterPolicy("regex"); + return new DefaultInlineConstraintResolver(accessor, serviceProvider); } } From d836911d19cb62844bd7a7df87afe9f4c174df57 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 12:30:59 +1100 Subject: [PATCH 15/37] Update src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs Co-authored-by: James Newton-King --- .../DependencyInjection/RoutingServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index b81f3e480e1b..e19907b54bd7 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -30,6 +30,7 @@ public static IServiceCollection AddRouting(this IServiceCollection services) { return services.AddRoutingCore().Configure(options => { + // Replace RegexErrorStubRouteConstraint with the real regex constraint. options.SetParameterPolicy("regex"); }); } From 233903a7194f27c49efd416c030842c2a82d8767 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 12:31:12 +1100 Subject: [PATCH 16/37] Update src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs Co-authored-by: James Newton-King --- .../DependencyInjection/RoutingServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index e19907b54bd7..b037befd439b 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -130,6 +130,7 @@ public static IServiceCollection AddRouting( { return services.AddRoutingCore().Configure(options => { + // Replace RegexErrorStubRouteConstraint with the real regex constraint. options.SetParameterPolicy("regex"); configureOptions(options); }); From 91b00aba766b00adc540793bc2b5750aed4ac0d6 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 6 Feb 2023 12:31:47 +1100 Subject: [PATCH 17/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: James Newton-King --- src/Http/Routing/src/RouteOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 66ef1888858f..e10baddca360 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -163,7 +163,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint { - public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string regexPattern) + public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string _) { throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); } From a18ac7c620fef7dd68d017961f2d357ccfbebee9 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Thu, 9 Feb 2023 18:50:26 +1100 Subject: [PATCH 18/37] Rename AddRoutingCore to AddRoutingSlim. --- src/DefaultBuilder/src/WebHost.cs | 2 +- .../RoutingServiceCollectionExtensions.cs | 6 +++--- src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index cd2eadb5b802..5de8b3083c34 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -257,7 +257,7 @@ internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder) services.AddTransient(); services.AddTransient, ForwardedHeadersOptionsSetup>(); - services.AddRoutingCore(); + services.AddRoutingSlim(); }); } diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index b037befd439b..379cedb765d9 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static class RoutingServiceCollectionExtensions /// The so that additional calls can be chained. public static IServiceCollection AddRouting(this IServiceCollection services) { - return services.AddRoutingCore().Configure(options => + return services.AddRoutingSlim().Configure(options => { // Replace RegexErrorStubRouteConstraint with the real regex constraint. options.SetParameterPolicy("regex"); @@ -42,7 +42,7 @@ public static IServiceCollection AddRouting(this IServiceCollection services) /// /// The to add the services to. /// The so that additional calls can be chained. - public static IServiceCollection AddRoutingCore(this IServiceCollection services) + public static IServiceCollection AddRoutingSlim(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); @@ -128,7 +128,7 @@ public static IServiceCollection AddRouting( this IServiceCollection services, Action configureOptions) { - return services.AddRoutingCore().Configure(options => + return services.AddRoutingSlim().Configure(options => { // Replace RegexErrorStubRouteConstraint with the real regex constraint. options.SetParameterPolicy("regex"); diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 277dc63d5f83..a3607a2cc49f 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -1,4 +1,4 @@ #nullable enable -static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! Microsoft.AspNetCore.Routing.RouteHandlerServices static Microsoft.AspNetCore.Routing.RouteHandlerServices.Map(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler, System.Collections.Generic.IEnumerable! httpMethods, System.Func! populateMetadata, System.Func! createRequestDelegate) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingSlim(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! From a060ef76f0dae9b925113926a8555981513953f3 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 06:44:35 +1100 Subject: [PATCH 19/37] Revert back to AddRoutingCore(...). --- src/DefaultBuilder/src/WebHost.cs | 2 +- .../RoutingServiceCollectionExtensions.cs | 6 +++--- src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 5de8b3083c34..cd2eadb5b802 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -257,7 +257,7 @@ internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder) services.AddTransient(); services.AddTransient, ForwardedHeadersOptionsSetup>(); - services.AddRoutingSlim(); + services.AddRoutingCore(); }); } diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 379cedb765d9..b037befd439b 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static class RoutingServiceCollectionExtensions /// The so that additional calls can be chained. public static IServiceCollection AddRouting(this IServiceCollection services) { - return services.AddRoutingSlim().Configure(options => + return services.AddRoutingCore().Configure(options => { // Replace RegexErrorStubRouteConstraint with the real regex constraint. options.SetParameterPolicy("regex"); @@ -42,7 +42,7 @@ public static IServiceCollection AddRouting(this IServiceCollection services) /// /// The to add the services to. /// The so that additional calls can be chained. - public static IServiceCollection AddRoutingSlim(this IServiceCollection services) + public static IServiceCollection AddRoutingCore(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); @@ -128,7 +128,7 @@ public static IServiceCollection AddRouting( this IServiceCollection services, Action configureOptions) { - return services.AddRoutingSlim().Configure(options => + return services.AddRoutingCore().Configure(options => { // Replace RegexErrorStubRouteConstraint with the real regex constraint. options.SetParameterPolicy("regex"); diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index a3607a2cc49f..fff1ced433fd 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -1,4 +1,4 @@ #nullable enable Microsoft.AspNetCore.Routing.RouteHandlerServices static Microsoft.AspNetCore.Routing.RouteHandlerServices.Map(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler, System.Collections.Generic.IEnumerable! httpMethods, System.Func! populateMetadata, System.Func! createRequestDelegate) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! -static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingSlim(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.Extensions.DependencyInjection.RoutingServiceCollectionExtensions.AddRoutingCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! From 44f29ef6c509c539c7be3f61da53fc6a60236d74 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 09:26:13 +1100 Subject: [PATCH 20/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: Eric Erhardt --- src/Http/Routing/src/RouteOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index e10baddca360..44ae626ef851 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -163,7 +163,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint { - public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string _) + public RegexErrorStubRouteConstraint(string _) { throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); } From 63b6f5922de2c881ca1224622428ff236e8a5cb1 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 13:04:26 +1100 Subject: [PATCH 21/37] Update src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs Co-authored-by: Stephen Halter --- .../RoutingServiceCollectionExtensions.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index b037befd439b..ecd51116f5f9 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -128,11 +128,12 @@ public static IServiceCollection AddRouting( this IServiceCollection services, Action configureOptions) { - return services.AddRoutingCore().Configure(options => - { - // Replace RegexErrorStubRouteConstraint with the real regex constraint. - options.SetParameterPolicy("regex"); - configureOptions(options); - }); + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configureOptions); + + services.AddRouting(); + services.Configure(configureOptions); + + return services; } } From bfcc4ffbab46234545c00daa1e3ee00f87d8022b Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 13:05:14 +1100 Subject: [PATCH 22/37] Moved error stub to separate file. --- .../RegexErrorStubRouteConstraint.cs | 22 +++++++++++++++++++ src/Http/Routing/src/RouteOptions.cs | 14 ------------ 2 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs diff --git a/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs b/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs new file mode 100644 index 000000000000..161bf8ac30f3 --- /dev/null +++ b/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Routing.Constraints; + +internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint +{ + public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string _) + { + throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); + } + + bool IRouteConstraint.Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) + { + // Should never get called, but is same as throw in constructor in case constructor is changed. + throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); + } +} diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index e10baddca360..22d3f7ed9714 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -160,17 +160,3 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic constraintMap[text] = typeof(TConstraint); } } - -internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint -{ - public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string _) - { - throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); - } - - bool IRouteConstraint.Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) - { - // Should never get called, but is same as throw in constructor in case constructor is changed. - throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); - } -} From 5b36ab893b78865c78f83b16f6c5a39ac2577f55 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 14:24:08 +1100 Subject: [PATCH 23/37] Responding to PR feedback around handling edge cases. --- .../RoutingServiceCollectionExtensions.cs | 13 ++- ...RoutingServiceCollectionExtensionsTests.cs | 90 +++++++++++++++++++ 2 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index ecd51116f5f9..58f8ea2475db 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -30,8 +30,15 @@ public static IServiceCollection AddRouting(this IServiceCollection services) { return services.AddRoutingCore().Configure(options => { - // Replace RegexErrorStubRouteConstraint with the real regex constraint. - options.SetParameterPolicy("regex"); + var existingRegexConstraintType = options.TrimmerSafeConstraintMap["regex"]; + + // Don't override regex constraint if it has already been overridden + // this behavior here is just to add it back in if someone calls AddRouting(...) + // after setting up routing with AddRoutingCore(...). + if (existingRegexConstraintType == typeof(RegexErrorStubRouteConstraint)) + { + options.SetParameterPolicy("regex"); + } }); } @@ -131,8 +138,8 @@ public static IServiceCollection AddRouting( ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configureOptions); - services.AddRouting(); services.Configure(configureOptions); + services.AddRouting(); return services; } diff --git a/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs b/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs new file mode 100644 index 000000000000..c52db0b1ca13 --- /dev/null +++ b/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Routing; +public class RoutingServiceCollectionExtensionsTests +{ + [Fact] + public void AddRouting_ThrowsOnNull_ServicesParameter() + { + var ex = Record.Exception(() => + { + RoutingServiceCollectionExtensions.AddRouting(null); + }); + + Assert.IsType(ex); + Assert.Equal("services", (ex as ArgumentNullException).ParamName); + } + + [Fact] + public void AddRoutingWithOptions_ThrowsOnNull_ConfigureOptionsParameter() + { + var services = new ServiceCollection(); + + var ex = Record.Exception(() => + { + RoutingServiceCollectionExtensions.AddRouting(services, null); + }); + + Assert.IsType(ex); + Assert.Equal("configureOptions", (ex as ArgumentNullException).ParamName); + } + + [Fact] + public void AddRoutingWithOptions_ThrowsOnNull_ServicesParameter() + { + var ex = Record.Exception(() => + { + RoutingServiceCollectionExtensions.AddRouting(null, options => { }); + }); + + Assert.IsType(ex); + Assert.Equal("services", (ex as ArgumentNullException).ParamName); + } + + public class DummyRegexRouteConstraint : IRouteConstraint + { + public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) + { + return true; + } + } + + [Fact] + public void AddRouting_DoesNot_Replace_Existing_RouteConstraint() + { + // Arrange + var services = new ServiceCollection(); + services.AddOptions(); + + services.Configure(options => + { + Console.Write("First!"); + }); + + + services.Configure(options => + { + Console.Write("Second!"); + options.SetParameterPolicy("regex"); + }); + + + services.AddRouting(); + var provider = services.BuildServiceProvider(); + + // Assert + var options = provider.GetService>(); + var regexRouteConstraintType = options.Value.ConstraintMap["regex"]; + Assert.Equal(typeof(DummyRegexRouteConstraint), regexRouteConstraintType); + } +} From 346876ade86b2b06fa44a7f7a98aa65bf80c7a32 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 14:31:39 +1100 Subject: [PATCH 24/37] Swap out call to AddRouting(...) with call to Configure(...) --- src/DefaultBuilder/src/WebApplicationBuilder.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 5638462177c4..16e851ec31d5 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -61,7 +63,10 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action { - services.AddRouting(); + services.Configure(routeOptions => + { + routeOptions.SetParameterPolicy("regex"); + }); }); // Runs inline. From 029b2dcac8f3f652ad9f2b0948f971f96cc4b480 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 15:00:24 +1100 Subject: [PATCH 25/37] Make sure WebHost.CreateDefaultBuilder(...) configures RegexInlineRouteConstraint and add test to that effect. --- src/DefaultBuilder/src/WebHost.cs | 9 +++++++++ .../Microsoft.AspNetCore.Tests/WebHostTests.cs | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index cd2eadb5b802..60a225eadec5 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting.StaticWebAssets; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -225,6 +226,14 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder) ConfigureWebDefaultsCore(builder); + builder.ConfigureServices(services => + { + services.Configure(routeOptions => + { + routeOptions.SetParameterPolicy("regex"); + }); + }); + builder .UseIIS() .UseIISIntegration(); diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebHostTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebHostTests.cs index a72cf88051cc..2cfebc44d4bd 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebHostTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebHostTests.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.HostFiltering; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Configuration; @@ -110,6 +111,19 @@ public void CreateDefaultBuilder_RegistersEventSourceLogger() args.Payload.OfType().Any(p => p.Contains("Request starting"))); } + [Fact] + public void WebHost_CreateDefaultBuilder_ConfiguresRegexInlineRouteConstraint_ByDefault() + { + var host = WebHost.CreateDefaultBuilder() + .Configure(_ => { }) + .Build(); + + var routeOptions = host.Services.GetService>(); + + Assert.True(routeOptions.Value.ConstraintMap.ContainsKey("regex")); + Assert.Equal(typeof(RegexInlineRouteConstraint), routeOptions.Value.ConstraintMap["regex"]); + } + private class TestEventListener : EventListener { private volatile bool _disposed; From 4b7991d10b3d42a75ce2240c5499777c079f2116 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 10 Feb 2023 15:49:07 +1100 Subject: [PATCH 26/37] Fix using constraint build error. --- src/Http/Routing/src/RouteOptions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index 22d3f7ed9714..e8aa71fee554 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -3,8 +3,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Constraints; namespace Microsoft.AspNetCore.Routing; From dc6c14347bc65274015e3b8ca01a13f1f80a1b7a Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sat, 11 Feb 2023 12:35:27 +1100 Subject: [PATCH 27/37] Update src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs Co-authored-by: Eric Erhardt --- .../Routing/src/Constraints/RegexErrorStubRouteConstraint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs b/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs index 161bf8ac30f3..695fd4a1b80b 100644 --- a/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs +++ b/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Routing.Constraints; internal sealed class RegexErrorStubRouteConstraint : IRouteConstraint { - public RegexErrorStubRouteConstraint([StringSyntax(StringSyntaxAttribute.Regex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] string _) + public RegexErrorStubRouteConstraint(string _) { throw new InvalidOperationException(Resources.RegexRouteContraint_NotConfigured); } From ae15aecf304942b94b0c471b50a499859af33942 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 13 Feb 2023 10:48:48 +1100 Subject: [PATCH 28/37] Update src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs Co-authored-by: Stephen Halter --- .../test/UnitTests/RoutingServiceCollectionExtensionsTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs b/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs index c52db0b1ca13..c87c62d1a42d 100644 --- a/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs +++ b/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs @@ -68,7 +68,6 @@ public void AddRouting_DoesNot_Replace_Existing_RouteConstraint() services.Configure(options => { - Console.Write("First!"); }); From d24a958438a7ec1d57b20e3d64d3f675c5374829 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 13 Feb 2023 10:50:06 +1100 Subject: [PATCH 29/37] Update src/Http/Routing/src/RouteOptions.cs Co-authored-by: Eric Erhardt --- src/Http/Routing/src/RouteOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs index e8aa71fee554..f43de9171ac1 100644 --- a/src/Http/Routing/src/RouteOptions.cs +++ b/src/Http/Routing/src/RouteOptions.cs @@ -153,7 +153,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic _constraintTypeMap[token] = type; } - private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TConstraint>(IDictionary constraintMap, string text) where TConstraint : IRouteConstraint + private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TConstraint>(Dictionary constraintMap, string text) where TConstraint : IRouteConstraint { constraintMap[text] = typeof(TConstraint); } From e0ca212d6aa9e7f4b8366d7da9b5fba035400c7c Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 13 Feb 2023 10:51:31 +1100 Subject: [PATCH 30/37] Update src/DefaultBuilder/src/WebApplicationBuilder.cs Co-authored-by: Stephen Halter --- src/DefaultBuilder/src/WebApplicationBuilder.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 16e851ec31d5..bb14bd5df616 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -61,14 +61,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action { - webHostBuilder.ConfigureServices(services => - { - services.Configure(routeOptions => - { - routeOptions.SetParameterPolicy("regex"); - }); - }); - // Runs inline. webHostBuilder.Configure(ConfigureApplication); From aa74554c3b9a8e75a6c6e9ea24e77b3924292d7c Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 13 Feb 2023 11:07:20 +1100 Subject: [PATCH 31/37] Updates from PR feedback. --- src/DefaultBuilder/src/WebApplicationBuilder.cs | 2 -- .../src/Constraints/RegexErrorStubRouteConstraint.cs | 2 -- .../UnitTests/RoutingServiceCollectionExtensionsTests.cs | 8 +------- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index bb14bd5df616..70d3ac7fc250 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -5,8 +5,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs b/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs index 695fd4a1b80b..aeb0324b0596 100644 --- a/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs +++ b/src/Http/Routing/src/Constraints/RegexErrorStubRouteConstraint.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Constraints; diff --git a/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs b/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs index c87c62d1a42d..18a3fa0e44e9 100644 --- a/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs +++ b/src/Http/Routing/test/UnitTests/RoutingServiceCollectionExtensionsTests.cs @@ -68,16 +68,10 @@ public void AddRouting_DoesNot_Replace_Existing_RouteConstraint() services.Configure(options => { - }); - - - services.Configure(options => - { - Console.Write("Second!"); options.SetParameterPolicy("regex"); }); - + // Act services.AddRouting(); var provider = services.BuildServiceProvider(); From 94dcdefb35e278be6c92ad1baf7105d3e62c93a8 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Tue, 14 Feb 2023 13:08:08 +1100 Subject: [PATCH 32/37] Revert back to calling AddRouting(...) from WebHost for now. --- src/DefaultBuilder/src/WebHost.cs | 6 +---- .../RoutingServiceCollectionExtensions.cs | 16 +++---------- .../src/RegexInlineRouteConstraintSetup.cs | 24 +++++++++++++++++++ 3 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 60a225eadec5..437e76894219 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Hosting.StaticWebAssets; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -228,10 +227,7 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder) builder.ConfigureServices(services => { - services.Configure(routeOptions => - { - routeOptions.SetParameterPolicy("regex"); - }); + services.AddRouting(); }); builder diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 58f8ea2475db..54829e8761d6 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -3,7 +3,6 @@ using System.Collections.ObjectModel; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Patterns; @@ -28,18 +27,9 @@ public static class RoutingServiceCollectionExtensions /// The so that additional calls can be chained. public static IServiceCollection AddRouting(this IServiceCollection services) { - return services.AddRoutingCore().Configure(options => - { - var existingRegexConstraintType = options.TrimmerSafeConstraintMap["regex"]; - - // Don't override regex constraint if it has already been overridden - // this behavior here is just to add it back in if someone calls AddRouting(...) - // after setting up routing with AddRoutingCore(...). - if (existingRegexConstraintType == typeof(RegexErrorStubRouteConstraint)) - { - options.SetParameterPolicy("regex"); - } - }); + services.AddRoutingCore(); + services.TryAddEnumerable(ServiceDescriptor.Singleton, RegexInlineRouteConstraintSetup>()); + return services; } /// diff --git a/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs b/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs new file mode 100644 index 000000000000..fec1add3f9dc --- /dev/null +++ b/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Routing.Constraints; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.DependencyInjection; + +internal sealed class RegexInlineRouteConstraintSetup : IPostConfigureOptions +{ + public void PostConfigure(string? name, RouteOptions options) + { + var existingRegexConstraintType = options.TrimmerSafeConstraintMap["regex"]; + + // Don't override regex constraint if it has already been overridden + // this behavior here is just to add it back in if someone calls AddRouting(...) + // after setting up routing with AddRoutingCore(...). + if (existingRegexConstraintType == typeof(RegexErrorStubRouteConstraint)) + { + options.SetParameterPolicy("regex"); + } + } +} From f2cf03e1fc18f090ca48ceea4941bf17231472ad Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Thu, 16 Feb 2023 19:29:48 +1100 Subject: [PATCH 33/37] Change back to IConfigureOptions. --- .../DependencyInjection/RoutingServiceCollectionExtensions.cs | 2 +- src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 54829e8761d6..01d332757839 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static class RoutingServiceCollectionExtensions public static IServiceCollection AddRouting(this IServiceCollection services) { services.AddRoutingCore(); - services.TryAddEnumerable(ServiceDescriptor.Singleton, RegexInlineRouteConstraintSetup>()); + services.TryAddEnumerable(ServiceDescriptor.Singleton, RegexInlineRouteConstraintSetup>()); return services; } diff --git a/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs b/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs index fec1add3f9dc..478e4834d8ac 100644 --- a/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs +++ b/src/Http/Routing/src/RegexInlineRouteConstraintSetup.cs @@ -7,9 +7,9 @@ namespace Microsoft.Extensions.DependencyInjection; -internal sealed class RegexInlineRouteConstraintSetup : IPostConfigureOptions +internal sealed class RegexInlineRouteConstraintSetup : IConfigureOptions { - public void PostConfigure(string? name, RouteOptions options) + public void Configure(RouteOptions options) { var existingRegexConstraintType = options.TrimmerSafeConstraintMap["regex"]; From 8819aae9b22326029344099036455edd0de323fe Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 17 Feb 2023 13:07:07 +1100 Subject: [PATCH 34/37] We did it! --- src/DefaultBuilder/src/WebHost.cs | 11 ++++++----- .../Routing/src/Microsoft.AspNetCore.Routing.csproj | 1 + .../Mvc/test/MvcServiceCollectionExtensionsTest.cs | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 437e76894219..0c23a167c40a 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -223,9 +223,7 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder) } }); - ConfigureWebDefaultsCore(builder); - - builder.ConfigureServices(services => + ConfigureWebDefaultsCore(builder, services => { services.AddRouting(); }); @@ -235,7 +233,7 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder) .UseIISIntegration(); } - internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder) + internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder, Action? configureRouting = default) { builder.UseKestrel((builderContext, options) => { @@ -262,7 +260,10 @@ internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder) services.AddTransient(); services.AddTransient, ForwardedHeadersOptionsSetup>(); - services.AddRoutingCore(); + if (configureRouting == default) + { + services.AddRoutingCore(); + } }); } diff --git a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj index 068c308b367c..6b8a4ffc52f4 100644 --- a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj +++ b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj @@ -50,5 +50,6 @@ + diff --git a/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs b/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs index 6a450fa381c0..15a46705ed8d 100644 --- a/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs +++ b/src/Mvc/Mvc/test/MvcServiceCollectionExtensionsTest.cs @@ -467,6 +467,7 @@ private Dictionary MultiRegistrationServiceTypes { typeof(MvcCoreRouteOptionsSetup), typeof(MvcCoreRouteOptionsSetup), + typeof(RegexInlineRouteConstraintSetup), } }, { From b9e4f5a08327a72752c4d38112ab56e8d9151210 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 17 Feb 2023 13:17:01 +1100 Subject: [PATCH 35/37] Forogt to actually call configureRouting(...) :) --- src/DefaultBuilder/src/WebHost.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 0c23a167c40a..25683c8efa07 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -264,6 +264,10 @@ internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder, Action Date: Fri, 17 Feb 2023 13:26:25 +1100 Subject: [PATCH 36/37] Update src/DefaultBuilder/src/WebHost.cs Co-authored-by: James Newton-King --- src/DefaultBuilder/src/WebHost.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index 25683c8efa07..d09940061c02 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -260,7 +260,10 @@ internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder, Action(); services.AddTransient, ForwardedHeadersOptionsSetup>(); - if (configureRouting == default) + // Provide a way for the default host builder to configure routing. This probably means calling AddRouting. + // A lambda is used here because we don't want to reference AddRouting directly because of trimming. + // This avoids the overhead of calling AddRoutingCore multiple times on app startup. + if (configureRouting == null) { services.AddRoutingCore(); } From 9adca9c7939913f6396a72d6ae2c97e0afa536a8 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 17 Feb 2023 13:26:44 +1100 Subject: [PATCH 37/37] Update src/DefaultBuilder/src/WebHost.cs Co-authored-by: James Newton-King --- src/DefaultBuilder/src/WebHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DefaultBuilder/src/WebHost.cs b/src/DefaultBuilder/src/WebHost.cs index d09940061c02..7e21713ae774 100644 --- a/src/DefaultBuilder/src/WebHost.cs +++ b/src/DefaultBuilder/src/WebHost.cs @@ -233,7 +233,7 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder) .UseIISIntegration(); } - internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder, Action? configureRouting = default) + internal static void ConfigureWebDefaultsCore(IWebHostBuilder builder, Action? configureRouting = null) { builder.UseKestrel((builderContext, options) => {