From 6c49e7b2eb66a5f492e577fc0d13ca21ebb7db00 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Tue, 23 May 2023 16:04:27 +1000 Subject: [PATCH] DisableInferBodyFromParameters in RDG. (#48269) * DisableInferBodyFromParameters in RDG. --- .../gen/RequestDelegateGenerator.cs | 9 +- .../Emitters/EmitterContext.cs | 1 + .../Emitters/EndpointEmitter.cs | 5 +- .../Emitters/EndpointParameterEmitter.cs | 35 ++ .../EndpointParameter.cs | 19 +- .../EndpointParameterSource.cs | 1 + .../StaticRouteHandlerModel.Emitter.cs | 4 +- ...MapAction_BindAsync_Snapshot.generated.txt | 72 ++- ...Param_ComplexReturn_Snapshot.generated.txt | 8 +- ...Header_ComplexTypeArrayParam.generated.txt | 4 +- ...der_NullableStringArrayParam.generated.txt | 4 +- ...licitHeader_StringArrayParam.generated.txt | 4 +- ...tQuery_ComplexTypeArrayParam.generated.txt | 4 +- ...ery_NullableStringArrayParam.generated.txt | 4 +- ...plicitQuery_StringArrayParam.generated.txt | 4 +- ...eParam_SimpleReturn_Snapshot.generated.txt | 12 +- ...Source_SimpleReturn_Snapshot.generated.txt | 24 +- ...tQuery_ComplexTypeArrayParam.generated.txt | 6 +- ...ery_NullableStringArrayParam.generated.txt | 145 ++++- ...gArrayParam_EmptyQueryValues.generated.txt | 145 ++++- ...ngArrayParam_QueryNotPresent.generated.txt | 145 ++++- ...plicitQuery_StringArrayParam.generated.txt | 145 ++++- ...ce_HandlesBothJsonAndService.generated.txt | 4 +- ...pecialTypeParam_StringReturn.generated.txt | 4 +- ...ipleStringParam_StringReturn.generated.txt | 4 +- ...aram_StringReturn_WithFilter.generated.txt | 4 +- ...n_ReturnsString_Has_Metadata.generated.txt | 4 +- ...ion_ReturnsTodo_Has_Metadata.generated.txt | 4 +- ...onProblemResult_Has_Metadata.generated.txt | 4 +- ..._ReturnsVoid_Has_No_Metadata.generated.txt | 4 +- ...omplexTypeParam_StringReturn.generated.txt | 4 +- ...SingleEnumParam_StringReturn.generated.txt | 4 +- ...ngValueProvided_StringReturn.generated.txt | 4 +- ...MetadataEmitter_Has_Metadata.generated.txt | 4 +- ...AndBody_ShouldUseQueryString.generated.txt | 506 ++++++++++++++++++ ...AndBody_ShouldUseQueryString.generated.txt | 506 ++++++++++++++++++ ...String_AndBody_ShouldUseBody.generated.txt | 506 ++++++++++++++++++ ...String_AndBody_ShouldUseBody.generated.txt | 506 ++++++++++++++++++ ...String_AndBody_ShouldUseBody.generated.txt | 59 +- ...hArrayQueryString_ShouldFail.generated.txt | 59 +- ...pAction_NoParam_StringReturn.generated.txt | 16 +- ...tion_WithParams_StringReturn.generated.txt | 12 +- ...ateValidateGeneratedFormCode.generated.txt | 4 +- .../VerifyAsParametersBaseline.generated.txt | 22 +- .../RequestDelegateCreationTestBase.cs | 2 +- .../RequestDelegateCreationTests.Arrays.cs | 107 ++++ 46 files changed, 2968 insertions(+), 185 deletions(-) create mode 100644 src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt create mode 100644 src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt create mode 100644 src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt create mode 100644 src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs b/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs index ca07b2f53d6e..028c498ef51e 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs @@ -74,6 +74,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) codeWriter.StartBlock(); codeWriter.WriteLine(@"Debug.Assert(options != null, ""RequestDelegateFactoryOptions not found."");"); codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder != null, ""EndpointBuilder not found."");"); + codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder.ApplicationServices != null, ""ApplicationServices not found."");"); + codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder.FilterFactories != null, ""FilterFactories not found."");"); codeWriter.WriteLine($"var handler = ({endpoint.EmitHandlerDelegateType(considerOptionality: true)})del;"); codeWriter.WriteLine("EndpointFilterDelegate? filteredInvocation = null;"); if (endpoint.EmitterContext.RequiresLoggingHelper || endpoint.EmitterContext.HasJsonBodyOrService || endpoint.Response?.IsSerializableJsonResponse(out var _) is true) @@ -89,7 +91,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) codeWriter.WriteLine("var parameters = del.Method.GetParameters();"); } codeWriter.WriteLineNoTabs(string.Empty); - codeWriter.WriteLine("if (options?.EndpointBuilder?.FilterFactories.Count > 0)"); + codeWriter.WriteLine("if (options.EndpointBuilder.FilterFactories.Count > 0)"); codeWriter.StartBlock(); codeWriter.WriteLine(endpoint.Response?.IsAwaitable == true ? "filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(async ic =>" @@ -173,6 +175,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Select((endpoints, _) => { var hasJsonBodyOrService = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBodyOrService); + var hasJsonBodyOrQuery = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBodyOrQuery); var hasJsonBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBody); var hasFormBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasFormBody); var hasRouteOrQuery = endpoints.Any(endpoint => endpoint.EmitterContext.HasRouteOrQuery); @@ -196,7 +199,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) codeWriter.WriteLine(RequestDelegateGeneratorSources.WriteToResponseAsyncMethod); } - if (hasJsonBody || hasJsonBodyOrService) + if (hasJsonBody || hasJsonBodyOrService || hasJsonBodyOrQuery) { codeWriter.WriteLine(RequestDelegateGeneratorSources.TryResolveBodyAsyncMethod); } @@ -244,7 +247,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Select((endpoints, _) => { var hasFormBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasFormBody); - var hasJsonBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBody || endpoint.EmitterContext.HasJsonBodyOrService); + var hasJsonBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBody || endpoint.EmitterContext.HasJsonBodyOrService || endpoint.EmitterContext.HasJsonBodyOrQuery); var hasResponseMetadata = endpoints.Any(endpoint => endpoint.EmitterContext.HasResponseMetadata); var requiresPropertyAsParameterInfo = endpoints.Any(endpoint => endpoint.EmitterContext.RequiresPropertyAsParameterInfo); diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs index d30a9742ef6d..669df4470d4e 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs @@ -5,6 +5,7 @@ namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerM internal sealed class EmitterContext { public bool HasJsonBodyOrService { get; set; } + public bool HasJsonBodyOrQuery { get; set; } public bool HasJsonBody { get; set; } public bool HasFormBody { get; set; } public bool HasRouteOrQuery { get; set; } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs index ec30c03b7e11..b4e311b86ee4 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs @@ -48,6 +48,9 @@ internal static string EmitParameterPreparation(this IEnumerable(httpContext, logOrThrowExceptionHelper, {(endpointParameter.IsOptional ? "true" : "false")}, {SymbolDisplay.FormatLiteral(shortParameterTypeName, true)}, {SymbolDisplay.FormatLiteral(endpointParameter.SymbolName, true)})"; + var resolveBodyResult = $"{endpointParameter.SymbolName}_resolveBodyResult"; + codeWriter.WriteLine($"var {resolveBodyResult} = {assigningCode};"); + codeWriter.WriteLine($"{endpointParameter.EmitHandlerArgument()} = {resolveBodyResult}.Item2!;"); + + // If binding from the JSON body fails, we exit early. Don't + // set the status code here because assume it has been set by the + // TryResolveBody method. + codeWriter.WriteLine($"if (!{resolveBodyResult}.Item1)"); + codeWriter.StartBlock(); + codeWriter.WriteLine("return;"); + codeWriter.EndBlock(); + + codeWriter.EndBlock(); + } + internal static void EmitBindAsyncPreparation(this EndpointParameter endpointParameter, CodeWriter codeWriter) { var unwrappedType = endpointParameter.Type.UnwrapTypeSymbol(unwrapNullable: true); diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs index 940d643f0d3d..1d205605fc88 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs @@ -201,11 +201,12 @@ Type is not INamedTypeSymbol namedTypeSymbol || { Source = EndpointParameterSource.RouteOrQuery; } - else if (ShouldDisableInferredBodyParameters(endpoint.HttpMethod) && IsArray && ElementType.SpecialType == SpecialType.System_String) + else if (IsArray && ElementType.SpecialType == SpecialType.System_String) { - Source = EndpointParameterSource.Query; + endpoint.IsAwaitable = true; + Source = EndpointParameterSource.JsonBodyOrQuery; } - else if (ShouldDisableInferredBodyParameters(endpoint.HttpMethod) && SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_Extensions_Primitives_StringValues))) + else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_Extensions_Primitives_StringValues))) { Source = EndpointParameterSource.Query; IsStringValues = true; @@ -227,6 +228,7 @@ Type is not INamedTypeSymbol namedTypeSymbol || endpoint.EmitterContext.HasFormBody |= Source == EndpointParameterSource.FormBody; endpoint.EmitterContext.HasJsonBody |= Source == EndpointParameterSource.JsonBody; endpoint.EmitterContext.HasJsonBodyOrService |= Source == EndpointParameterSource.JsonBodyOrService; + endpoint.EmitterContext.HasJsonBodyOrQuery |= Source == EndpointParameterSource.JsonBodyOrQuery; } private static bool ImplementsIEndpointMetadataProvider(ITypeSymbol type, WellKnownTypes wellKnownTypes) @@ -235,17 +237,6 @@ private static bool ImplementsIEndpointMetadataProvider(ITypeSymbol type, WellKn private static bool ImplementsIEndpointParameterMetadataProvider(ITypeSymbol type, WellKnownTypes wellKnownTypes) => type.Implements(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IEndpointParameterMetadataProvider)); - private static bool ShouldDisableInferredBodyParameters(string httpMethod) - { - switch (httpMethod) - { - case "MapPut" or "MapPatch" or "MapPost": - return false; - default: - return true; - } - } - public ITypeSymbol Type { get; } public ITypeSymbol ElementType { get; } public bool IsEndpointMetadataProvider { get; } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameterSource.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameterSource.cs index 5a545a831dc0..d8410775a278 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameterSource.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameterSource.cs @@ -23,4 +23,5 @@ internal enum EndpointParameterSource // Used to track that the parameter is annotated with `AsParameters` and // can explode to multiple parameters AsParameters, + JsonBodyOrQuery, } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs index 0c0d11fecb02..4b6c84954241 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs @@ -271,7 +271,7 @@ public static void EmitJsonAcceptsMetadata(this Endpoint endpoint, CodeWriter co explicitBodyParameter = parameter; break; } - else if (parameter.Source == EndpointParameterSource.JsonBodyOrService) + else if (parameter.Source == EndpointParameterSource.JsonBodyOrService || parameter.Source == EndpointParameterSource.JsonBodyOrQuery) { potentialImplicitBodyParameters.Add(parameter); } @@ -311,7 +311,7 @@ public static void EmitJsonAcceptsMetadata(this Endpoint endpoint, CodeWriter co public static void EmitAcceptsMetadata(this Endpoint endpoint, CodeWriter codeWriter) { - var hasJsonBody = endpoint.EmitterContext.HasJsonBody || endpoint.EmitterContext.HasJsonBodyOrService; + var hasJsonBody = endpoint.EmitterContext.HasJsonBody || endpoint.EmitterContext.HasJsonBodyOrService || endpoint.EmitterContext.HasJsonBodyOrQuery; if (endpoint.EmitterContext.HasFormBody) { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt index c51031f9eb35..592e2fe14d5e 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt @@ -359,13 +359,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -448,13 +450,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -515,13 +519,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -604,13 +610,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -671,13 +679,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -760,13 +770,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -827,13 +839,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -916,13 +930,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -983,12 +999,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1071,12 +1089,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1137,12 +1157,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1225,12 +1247,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1291,13 +1315,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1380,13 +1406,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1447,12 +1475,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1535,12 +1565,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1601,13 +1633,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -1690,13 +1724,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var parameters = del.Method.GetParameters(); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt index d2b44024dfa9..75f8382b3c8b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt @@ -105,12 +105,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func>)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -181,12 +183,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func>)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt index 50752ba6ef3d..39c81fda6e1f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt @@ -104,6 +104,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -111,7 +113,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt index feddc45e748b..4bf769e24856 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt @@ -104,6 +104,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -111,7 +113,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt index 7d7a0d42d97f..fe16417083a2 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt @@ -104,6 +104,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -111,7 +113,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt index c8339e999c3a..96632fc9ce0e 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt @@ -104,6 +104,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -111,7 +113,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt index d4286778fe3b..43a3711dc3b9 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt @@ -104,13 +104,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt index 7965ed87b87b..14022e665a58 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt @@ -104,13 +104,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt index 5c9abf552f9c..958cec399170 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt @@ -134,12 +134,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -200,12 +202,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func, global::System.String>)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -266,12 +270,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func, global::System.String>)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt index 090e087af40c..80e39111d8d4 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt @@ -104,12 +104,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -184,12 +186,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -264,12 +268,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -352,13 +358,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options?.RouteParameterNames); + var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options.RouteParameterNames); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -433,13 +441,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options?.RouteParameterNames); + var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options.RouteParameterNames); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt index 769e5c359936..7d3b6005f7fb 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt @@ -104,15 +104,17 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - var p_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("p", options?.RouteParameterNames); + var p_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("p", options.RouteParameterNames); var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt index d4286778fe3b..8ddc4074e5d5 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt @@ -97,6 +97,19 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: typeof(global::System.Int32), statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }, @@ -104,13 +117,16 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -124,31 +140,55 @@ namespace Microsoft.AspNetCore.Http.Generated handler.Method); } - Task RequestHandler(HttpContext httpContext) + async Task RequestHandler(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String?[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string?[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { httpContext.Response.StatusCode = 400; - return Task.CompletedTask; + return; } httpContext.Response.ContentType ??= "application/json"; var result = handler(p_local); - return httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); + await httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); } async Task RequestHandlerFiltered(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String?[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string?[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { @@ -213,9 +253,90 @@ namespace Microsoft.AspNetCore.Http.Generated } } + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } } + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata { public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt index d4286778fe3b..8ddc4074e5d5 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt @@ -97,6 +97,19 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: typeof(global::System.Int32), statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }, @@ -104,13 +117,16 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -124,31 +140,55 @@ namespace Microsoft.AspNetCore.Http.Generated handler.Method); } - Task RequestHandler(HttpContext httpContext) + async Task RequestHandler(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String?[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string?[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { httpContext.Response.StatusCode = 400; - return Task.CompletedTask; + return; } httpContext.Response.ContentType ??= "application/json"; var result = handler(p_local); - return httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); + await httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); } async Task RequestHandlerFiltered(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String?[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string?[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { @@ -213,9 +253,90 @@ namespace Microsoft.AspNetCore.Http.Generated } } + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } } + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata { public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt index d4286778fe3b..8ddc4074e5d5 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt @@ -97,6 +97,19 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: typeof(global::System.Int32), statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }, @@ -104,13 +117,16 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -124,31 +140,55 @@ namespace Microsoft.AspNetCore.Http.Generated handler.Method); } - Task RequestHandler(HttpContext httpContext) + async Task RequestHandler(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String?[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string?[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { httpContext.Response.StatusCode = 400; - return Task.CompletedTask; + return; } httpContext.Response.ContentType ??= "application/json"; var result = handler(p_local); - return httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); + await httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); } async Task RequestHandlerFiltered(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string?[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String?[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string?[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { @@ -213,9 +253,90 @@ namespace Microsoft.AspNetCore.Http.Generated } } + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } } + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata { public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt index 7965ed87b87b..b5dea1129886 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt @@ -97,6 +97,19 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: typeof(global::System.Int32), statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }, @@ -104,13 +117,16 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -124,31 +140,55 @@ namespace Microsoft.AspNetCore.Http.Generated handler.Method); } - Task RequestHandler(HttpContext httpContext) + async Task RequestHandler(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { httpContext.Response.StatusCode = 400; - return Task.CompletedTask; + return; } httpContext.Response.ContentType ??= "application/json"; var result = handler(p_local); - return httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); + await httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); } async Task RequestHandlerFiltered(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = Query) - var p_raw = httpContext.Request.Query["p"]; - var p_temp = p_raw.ToArray(); - string[] p_local = p_temp!; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } if (wasParamCheckFailure) { @@ -213,9 +253,90 @@ namespace Microsoft.AspNetCore.Http.Generated } } + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } } + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata { public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt index 0b9abf4ba91d..2a0b75a08289 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt @@ -118,6 +118,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -126,7 +128,7 @@ namespace Microsoft.AspNetCore.Http.Generated var todo_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "Todo", "todo", serviceProviderIsService); var svc_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "TestService", "svc", serviceProviderIsService); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt index bb3f7d1a88e1..32dae45c9c97 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt @@ -104,10 +104,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt index a84bcd89ceda..d90d1f27bc2d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt @@ -104,12 +104,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt index beffb24eb86d..27719976db9f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt @@ -104,10 +104,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt index beffb24eb86d..27719976db9f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt @@ -104,10 +104,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt index 3c311eaae2ec..a3a32a6c7192 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt @@ -104,13 +104,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsValidationProblemResult_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsValidationProblemResult_Has_Metadata.generated.txt index 5ea238b3fbb1..a076e926a474 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsValidationProblemResult_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsValidationProblemResult_Has_Metadata.generated.txt @@ -104,10 +104,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsVoid_Has_No_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsVoid_Has_No_Metadata.generated.txt index c600bc7323b9..86c84aa50d7f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsVoid_Has_No_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsVoid_Has_No_Metadata.generated.txt @@ -103,10 +103,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt index dfe9f48217f0..8c50d89bc3f3 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt @@ -104,12 +104,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt index 5931f46bb814..68b39a418df1 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt @@ -104,12 +104,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt index ff0e515204ed..18c22b92b1a4 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt @@ -104,12 +104,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt index d51d5806bd87..848a39b5dd87 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt @@ -120,6 +120,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -127,7 +129,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serviceProviderIsService = serviceProvider?.GetService(); var x_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "CustomMetadataEmitter", "x", serviceProviderIsService); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt new file mode 100644 index 000000000000..0f9825ddd84a --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -0,0 +1,506 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable + +namespace Microsoft.AspNetCore.Builder +{ + %GENERATEDCODEATTRIBUTE% + internal sealed class SourceKey + { + public string Path { get; init; } + public int Line { get; init; } + + public SourceKey(string path, int line) + { + Path = path; + Line = line; + } + } + + // This class needs to be internal so that the compiled application + // has access to the strongly-typed endpoint definitions that are + // generated by the compiler so that they will be favored by + // overload resolution and opt the runtime in to the code generated + // implementation produced here. + %GENERATEDCODEATTRIBUTE% + internal static class GenerateRouteBuilderEndpoints + { + private static readonly string[] GetVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Get }; + private static readonly string[] PostVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Post }; + private static readonly string[] PutVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Put }; + private static readonly string[] DeleteVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Delete }; + private static readonly string[] PatchVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Patch }; + + internal static global::Microsoft.AspNetCore.Builder.RouteHandlerBuilder MapMethods( + this global::Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, + [global::System.Diagnostics.CodeAnalysis.StringSyntax("Route")] string pattern, + global::System.Collections.Generic.IEnumerable httpMethods, + global::System.Func handler, + [global::System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [global::System.Runtime.CompilerServices.CallerLineNumber]int lineNumber = 0) + { + return global::Microsoft.AspNetCore.Http.Generated.GeneratedRouteBuilderExtensionsCore.MapCore( + endpoints, + pattern, + handler, + httpMethods, + filePath, + lineNumber); + } + + } +} + +namespace Microsoft.AspNetCore.Http.Generated +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Text.Json; + using System.Text.Json.Serialization.Metadata; + using System.Threading.Tasks; + using System.IO; + using Microsoft.AspNetCore.Routing; + using Microsoft.AspNetCore.Routing.Patterns; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Json; + using Microsoft.AspNetCore.Http.Metadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.FileProviders; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Primitives; + using Microsoft.Extensions.Options; + + using MetadataPopulator = System.Func; + using RequestDelegateFactoryFunc = System.Func; + + file static class GeneratedRouteBuilderExtensionsCore + { + + private static readonly Dictionary<(string, int), (MetadataPopulator, RequestDelegateFactoryFunc)> map = new() + { + [(@"TestMapActions.cs", 25)] = ( + (methodInfo, options) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } + options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: null, statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; + }, + (del, options, inferredMetadataResult) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); + var handler = (System.Func)del; + EndpointFilterDelegate? filteredInvocation = null; + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); + + if (options.EndpointBuilder.FilterFactories.Count > 0) + { + filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => + { + if (ic.HttpContext.Response.StatusCode == 400) + { + return ValueTask.FromResult(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0)!)); + }, + options.EndpointBuilder, + handler.Method); + } + + async Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + await httpContext.Response.WriteAsync(result); + } + + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable? httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", + Justification = "The 'JsonSerializer.IsReflectionEnabledByDefault' feature switch, which is set to false by default for trimmed ASP.NET apps, ensures the JsonSerializer doesn't use Reflection.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "See above.")] + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } + + } + + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } + file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata + { + public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) + { + Type = type; + StatusCode = statusCode; + ContentTypes = contentTypes; + } + + public Type? Type { get; } + + public int StatusCode { get; } + + public IEnumerable ContentTypes { get; } + } + + file static class GeneratedMetadataConstants + { + public static readonly string[] JsonContentType = new [] { "application/json" }; + public static readonly string[] PlaintextContentType = new [] { "text/plain" }; + public static readonly string[] FormFileContentType = new[] { "multipart/form-data" }; + public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; + } + + + file sealed class LogOrThrowExceptionHelper + { + private readonly ILogger? _rdgLogger; + private readonly bool _shouldThrow; + + public LogOrThrowExceptionHelper(IServiceProvider? serviceProvider, RequestDelegateFactoryOptions? options) + { + var loggerFactory = serviceProvider?.GetRequiredService(); + _rdgLogger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator"); + _shouldThrow = options?.ThrowOnBadRequest ?? false; + } + + public void RequestBodyIOException(IOException exception) + { + if (_rdgLogger != null) + { + _requestBodyIOException(_rdgLogger, exception); + } + } + + private static readonly Action _requestBodyIOException = + LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestBodyIOException"), "Reading the request body failed with an IOException."); + + public void InvalidJsonRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as JSON.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidJsonRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidJsonRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(2, "InvalidJsonRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as JSON."); + + public void ParameterBindingFailed(string parameterTypeName, string parameterName, string sourceValue) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to bind parameter \"{0} {1}\" from \"{2}\".", parameterTypeName, parameterName, sourceValue); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _parameterBindingFailed(_rdgLogger, parameterTypeName, parameterName, sourceValue, null); + } + } + + private static readonly Action _parameterBindingFailed = + LoggerMessage.Define(LogLevel.Debug, new EventId(3, "ParameterBindingFailed"), "Failed to bind parameter \"{ParameterType} {ParameterName}\" from \"{SourceValue}\"."); + + public void RequiredParameterNotProvided(string parameterTypeName, string parameterName, string source) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Required parameter \"{0} {1}\" was not provided from {2}.", parameterTypeName, parameterName, source); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _requiredParameterNotProvided(_rdgLogger, parameterTypeName, parameterName, source, null); + } + } + + private static readonly Action _requiredParameterNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(4, "RequiredParameterNotProvided"), "Required parameter \"{ParameterType} {ParameterName}\" was not provided from {Source}."); + + public void ImplicitBodyNotProvided(string parameterName) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Implicit body inferred for parameter \"{0}\" but no body was provided. Did you mean to use a Service instead?", parameterName); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _implicitBodyNotProvided(_rdgLogger, parameterName, null); + } + } + + private static readonly Action _implicitBodyNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, "ImplicitBodyNotProvided"), "Implicit body inferred for parameter \"{ParameterName}\" but no body was provided. Did you mean to use a Service instead?"); + + public void UnexpectedJsonContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported JSON media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedJsonContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedJsonContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(6, "UnexpectedContentType"), "Expected a supported JSON media type but got \"{ContentType}\"."); + + public void UnexpectedNonFormContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported form media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedNonFormContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedNonFormContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(7, "UnexpectedNonFormContentType"), "Expected a supported form media type but got \"{ContentType}\"."); + + public void InvalidFormRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as form.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidFormRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidFormRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(8, "InvalidFormRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as form."); + } +} diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt new file mode 100644 index 000000000000..0f9825ddd84a --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -0,0 +1,506 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable + +namespace Microsoft.AspNetCore.Builder +{ + %GENERATEDCODEATTRIBUTE% + internal sealed class SourceKey + { + public string Path { get; init; } + public int Line { get; init; } + + public SourceKey(string path, int line) + { + Path = path; + Line = line; + } + } + + // This class needs to be internal so that the compiled application + // has access to the strongly-typed endpoint definitions that are + // generated by the compiler so that they will be favored by + // overload resolution and opt the runtime in to the code generated + // implementation produced here. + %GENERATEDCODEATTRIBUTE% + internal static class GenerateRouteBuilderEndpoints + { + private static readonly string[] GetVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Get }; + private static readonly string[] PostVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Post }; + private static readonly string[] PutVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Put }; + private static readonly string[] DeleteVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Delete }; + private static readonly string[] PatchVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Patch }; + + internal static global::Microsoft.AspNetCore.Builder.RouteHandlerBuilder MapMethods( + this global::Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, + [global::System.Diagnostics.CodeAnalysis.StringSyntax("Route")] string pattern, + global::System.Collections.Generic.IEnumerable httpMethods, + global::System.Func handler, + [global::System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [global::System.Runtime.CompilerServices.CallerLineNumber]int lineNumber = 0) + { + return global::Microsoft.AspNetCore.Http.Generated.GeneratedRouteBuilderExtensionsCore.MapCore( + endpoints, + pattern, + handler, + httpMethods, + filePath, + lineNumber); + } + + } +} + +namespace Microsoft.AspNetCore.Http.Generated +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Text.Json; + using System.Text.Json.Serialization.Metadata; + using System.Threading.Tasks; + using System.IO; + using Microsoft.AspNetCore.Routing; + using Microsoft.AspNetCore.Routing.Patterns; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Json; + using Microsoft.AspNetCore.Http.Metadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.FileProviders; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Primitives; + using Microsoft.Extensions.Options; + + using MetadataPopulator = System.Func; + using RequestDelegateFactoryFunc = System.Func; + + file static class GeneratedRouteBuilderExtensionsCore + { + + private static readonly Dictionary<(string, int), (MetadataPopulator, RequestDelegateFactoryFunc)> map = new() + { + [(@"TestMapActions.cs", 25)] = ( + (methodInfo, options) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } + options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: null, statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; + }, + (del, options, inferredMetadataResult) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); + var handler = (System.Func)del; + EndpointFilterDelegate? filteredInvocation = null; + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); + + if (options.EndpointBuilder.FilterFactories.Count > 0) + { + filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => + { + if (ic.HttpContext.Response.StatusCode == 400) + { + return ValueTask.FromResult(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0)!)); + }, + options.EndpointBuilder, + handler.Method); + } + + async Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + await httpContext.Response.WriteAsync(result); + } + + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable? httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", + Justification = "The 'JsonSerializer.IsReflectionEnabledByDefault' feature switch, which is set to false by default for trimmed ASP.NET apps, ensures the JsonSerializer doesn't use Reflection.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "See above.")] + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } + + } + + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } + file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata + { + public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) + { + Type = type; + StatusCode = statusCode; + ContentTypes = contentTypes; + } + + public Type? Type { get; } + + public int StatusCode { get; } + + public IEnumerable ContentTypes { get; } + } + + file static class GeneratedMetadataConstants + { + public static readonly string[] JsonContentType = new [] { "application/json" }; + public static readonly string[] PlaintextContentType = new [] { "text/plain" }; + public static readonly string[] FormFileContentType = new[] { "multipart/form-data" }; + public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; + } + + + file sealed class LogOrThrowExceptionHelper + { + private readonly ILogger? _rdgLogger; + private readonly bool _shouldThrow; + + public LogOrThrowExceptionHelper(IServiceProvider? serviceProvider, RequestDelegateFactoryOptions? options) + { + var loggerFactory = serviceProvider?.GetRequiredService(); + _rdgLogger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator"); + _shouldThrow = options?.ThrowOnBadRequest ?? false; + } + + public void RequestBodyIOException(IOException exception) + { + if (_rdgLogger != null) + { + _requestBodyIOException(_rdgLogger, exception); + } + } + + private static readonly Action _requestBodyIOException = + LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestBodyIOException"), "Reading the request body failed with an IOException."); + + public void InvalidJsonRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as JSON.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidJsonRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidJsonRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(2, "InvalidJsonRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as JSON."); + + public void ParameterBindingFailed(string parameterTypeName, string parameterName, string sourceValue) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to bind parameter \"{0} {1}\" from \"{2}\".", parameterTypeName, parameterName, sourceValue); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _parameterBindingFailed(_rdgLogger, parameterTypeName, parameterName, sourceValue, null); + } + } + + private static readonly Action _parameterBindingFailed = + LoggerMessage.Define(LogLevel.Debug, new EventId(3, "ParameterBindingFailed"), "Failed to bind parameter \"{ParameterType} {ParameterName}\" from \"{SourceValue}\"."); + + public void RequiredParameterNotProvided(string parameterTypeName, string parameterName, string source) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Required parameter \"{0} {1}\" was not provided from {2}.", parameterTypeName, parameterName, source); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _requiredParameterNotProvided(_rdgLogger, parameterTypeName, parameterName, source, null); + } + } + + private static readonly Action _requiredParameterNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(4, "RequiredParameterNotProvided"), "Required parameter \"{ParameterType} {ParameterName}\" was not provided from {Source}."); + + public void ImplicitBodyNotProvided(string parameterName) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Implicit body inferred for parameter \"{0}\" but no body was provided. Did you mean to use a Service instead?", parameterName); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _implicitBodyNotProvided(_rdgLogger, parameterName, null); + } + } + + private static readonly Action _implicitBodyNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, "ImplicitBodyNotProvided"), "Implicit body inferred for parameter \"{ParameterName}\" but no body was provided. Did you mean to use a Service instead?"); + + public void UnexpectedJsonContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported JSON media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedJsonContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedJsonContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(6, "UnexpectedContentType"), "Expected a supported JSON media type but got \"{ContentType}\"."); + + public void UnexpectedNonFormContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported form media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedNonFormContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedNonFormContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(7, "UnexpectedNonFormContentType"), "Expected a supported form media type but got \"{ContentType}\"."); + + public void InvalidFormRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as form.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidFormRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidFormRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(8, "InvalidFormRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as form."); + } +} diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt new file mode 100644 index 000000000000..0f9825ddd84a --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -0,0 +1,506 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable + +namespace Microsoft.AspNetCore.Builder +{ + %GENERATEDCODEATTRIBUTE% + internal sealed class SourceKey + { + public string Path { get; init; } + public int Line { get; init; } + + public SourceKey(string path, int line) + { + Path = path; + Line = line; + } + } + + // This class needs to be internal so that the compiled application + // has access to the strongly-typed endpoint definitions that are + // generated by the compiler so that they will be favored by + // overload resolution and opt the runtime in to the code generated + // implementation produced here. + %GENERATEDCODEATTRIBUTE% + internal static class GenerateRouteBuilderEndpoints + { + private static readonly string[] GetVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Get }; + private static readonly string[] PostVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Post }; + private static readonly string[] PutVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Put }; + private static readonly string[] DeleteVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Delete }; + private static readonly string[] PatchVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Patch }; + + internal static global::Microsoft.AspNetCore.Builder.RouteHandlerBuilder MapMethods( + this global::Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, + [global::System.Diagnostics.CodeAnalysis.StringSyntax("Route")] string pattern, + global::System.Collections.Generic.IEnumerable httpMethods, + global::System.Func handler, + [global::System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [global::System.Runtime.CompilerServices.CallerLineNumber]int lineNumber = 0) + { + return global::Microsoft.AspNetCore.Http.Generated.GeneratedRouteBuilderExtensionsCore.MapCore( + endpoints, + pattern, + handler, + httpMethods, + filePath, + lineNumber); + } + + } +} + +namespace Microsoft.AspNetCore.Http.Generated +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Text.Json; + using System.Text.Json.Serialization.Metadata; + using System.Threading.Tasks; + using System.IO; + using Microsoft.AspNetCore.Routing; + using Microsoft.AspNetCore.Routing.Patterns; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Json; + using Microsoft.AspNetCore.Http.Metadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.FileProviders; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Primitives; + using Microsoft.Extensions.Options; + + using MetadataPopulator = System.Func; + using RequestDelegateFactoryFunc = System.Func; + + file static class GeneratedRouteBuilderExtensionsCore + { + + private static readonly Dictionary<(string, int), (MetadataPopulator, RequestDelegateFactoryFunc)> map = new() + { + [(@"TestMapActions.cs", 25)] = ( + (methodInfo, options) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } + options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: null, statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; + }, + (del, options, inferredMetadataResult) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); + var handler = (System.Func)del; + EndpointFilterDelegate? filteredInvocation = null; + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); + + if (options.EndpointBuilder.FilterFactories.Count > 0) + { + filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => + { + if (ic.HttpContext.Response.StatusCode == 400) + { + return ValueTask.FromResult(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0)!)); + }, + options.EndpointBuilder, + handler.Method); + } + + async Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + await httpContext.Response.WriteAsync(result); + } + + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable? httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", + Justification = "The 'JsonSerializer.IsReflectionEnabledByDefault' feature switch, which is set to false by default for trimmed ASP.NET apps, ensures the JsonSerializer doesn't use Reflection.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "See above.")] + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } + + } + + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } + file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata + { + public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) + { + Type = type; + StatusCode = statusCode; + ContentTypes = contentTypes; + } + + public Type? Type { get; } + + public int StatusCode { get; } + + public IEnumerable ContentTypes { get; } + } + + file static class GeneratedMetadataConstants + { + public static readonly string[] JsonContentType = new [] { "application/json" }; + public static readonly string[] PlaintextContentType = new [] { "text/plain" }; + public static readonly string[] FormFileContentType = new[] { "multipart/form-data" }; + public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; + } + + + file sealed class LogOrThrowExceptionHelper + { + private readonly ILogger? _rdgLogger; + private readonly bool _shouldThrow; + + public LogOrThrowExceptionHelper(IServiceProvider? serviceProvider, RequestDelegateFactoryOptions? options) + { + var loggerFactory = serviceProvider?.GetRequiredService(); + _rdgLogger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator"); + _shouldThrow = options?.ThrowOnBadRequest ?? false; + } + + public void RequestBodyIOException(IOException exception) + { + if (_rdgLogger != null) + { + _requestBodyIOException(_rdgLogger, exception); + } + } + + private static readonly Action _requestBodyIOException = + LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestBodyIOException"), "Reading the request body failed with an IOException."); + + public void InvalidJsonRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as JSON.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidJsonRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidJsonRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(2, "InvalidJsonRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as JSON."); + + public void ParameterBindingFailed(string parameterTypeName, string parameterName, string sourceValue) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to bind parameter \"{0} {1}\" from \"{2}\".", parameterTypeName, parameterName, sourceValue); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _parameterBindingFailed(_rdgLogger, parameterTypeName, parameterName, sourceValue, null); + } + } + + private static readonly Action _parameterBindingFailed = + LoggerMessage.Define(LogLevel.Debug, new EventId(3, "ParameterBindingFailed"), "Failed to bind parameter \"{ParameterType} {ParameterName}\" from \"{SourceValue}\"."); + + public void RequiredParameterNotProvided(string parameterTypeName, string parameterName, string source) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Required parameter \"{0} {1}\" was not provided from {2}.", parameterTypeName, parameterName, source); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _requiredParameterNotProvided(_rdgLogger, parameterTypeName, parameterName, source, null); + } + } + + private static readonly Action _requiredParameterNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(4, "RequiredParameterNotProvided"), "Required parameter \"{ParameterType} {ParameterName}\" was not provided from {Source}."); + + public void ImplicitBodyNotProvided(string parameterName) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Implicit body inferred for parameter \"{0}\" but no body was provided. Did you mean to use a Service instead?", parameterName); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _implicitBodyNotProvided(_rdgLogger, parameterName, null); + } + } + + private static readonly Action _implicitBodyNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, "ImplicitBodyNotProvided"), "Implicit body inferred for parameter \"{ParameterName}\" but no body was provided. Did you mean to use a Service instead?"); + + public void UnexpectedJsonContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported JSON media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedJsonContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedJsonContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(6, "UnexpectedContentType"), "Expected a supported JSON media type but got \"{ContentType}\"."); + + public void UnexpectedNonFormContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported form media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedNonFormContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedNonFormContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(7, "UnexpectedNonFormContentType"), "Expected a supported form media type but got \"{ContentType}\"."); + + public void InvalidFormRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as form.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidFormRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidFormRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(8, "InvalidFormRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as form."); + } +} diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt new file mode 100644 index 000000000000..0f9825ddd84a --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -0,0 +1,506 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable + +namespace Microsoft.AspNetCore.Builder +{ + %GENERATEDCODEATTRIBUTE% + internal sealed class SourceKey + { + public string Path { get; init; } + public int Line { get; init; } + + public SourceKey(string path, int line) + { + Path = path; + Line = line; + } + } + + // This class needs to be internal so that the compiled application + // has access to the strongly-typed endpoint definitions that are + // generated by the compiler so that they will be favored by + // overload resolution and opt the runtime in to the code generated + // implementation produced here. + %GENERATEDCODEATTRIBUTE% + internal static class GenerateRouteBuilderEndpoints + { + private static readonly string[] GetVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Get }; + private static readonly string[] PostVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Post }; + private static readonly string[] PutVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Put }; + private static readonly string[] DeleteVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Delete }; + private static readonly string[] PatchVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Patch }; + + internal static global::Microsoft.AspNetCore.Builder.RouteHandlerBuilder MapMethods( + this global::Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, + [global::System.Diagnostics.CodeAnalysis.StringSyntax("Route")] string pattern, + global::System.Collections.Generic.IEnumerable httpMethods, + global::System.Func handler, + [global::System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [global::System.Runtime.CompilerServices.CallerLineNumber]int lineNumber = 0) + { + return global::Microsoft.AspNetCore.Http.Generated.GeneratedRouteBuilderExtensionsCore.MapCore( + endpoints, + pattern, + handler, + httpMethods, + filePath, + lineNumber); + } + + } +} + +namespace Microsoft.AspNetCore.Http.Generated +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Text.Json; + using System.Text.Json.Serialization.Metadata; + using System.Threading.Tasks; + using System.IO; + using Microsoft.AspNetCore.Routing; + using Microsoft.AspNetCore.Routing.Patterns; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Json; + using Microsoft.AspNetCore.Http.Metadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.FileProviders; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Primitives; + using Microsoft.Extensions.Options; + + using MetadataPopulator = System.Func; + using RequestDelegateFactoryFunc = System.Func; + + file static class GeneratedRouteBuilderExtensionsCore + { + + private static readonly Dictionary<(string, int), (MetadataPopulator, RequestDelegateFactoryFunc)> map = new() + { + [(@"TestMapActions.cs", 25)] = ( + (methodInfo, options) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 25)); + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var serviceProviderIsService = serviceProvider.GetRequiredService(); + var jsonBodyOrServiceTypeTuples = new (bool, Type)[] { + (false, typeof(global::System.String[])), + }; + foreach (var (isOptional, type) in jsonBodyOrServiceTypeTuples) + { + if (!serviceProviderIsService.IsService(type)) + { + options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); + break; + } + } + options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: null, statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; + }, + (del, options, inferredMetadataResult) => + { + Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); + Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); + var handler = (System.Func)del; + EndpointFilterDelegate? filteredInvocation = null; + var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; + var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); + + if (options.EndpointBuilder.FilterFactories.Count > 0) + { + filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => + { + if (ic.HttpContext.Response.StatusCode == 400) + { + return ValueTask.FromResult(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0)!)); + }, + options.EndpointBuilder, + handler.Method); + } + + async Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + await httpContext.Response.WriteAsync(result); + } + + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) + { + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } + } + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable? httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", + Justification = "The 'JsonSerializer.IsReflectionEnabledByDefault' feature switch, which is set to false by default for trimmed ASP.NET apps, ensures the JsonSerializer doesn't use Reflection.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "See above.")] + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBodyAsync(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType); + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + if (!isInferred) + { + logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body"); + } + else + { + logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName); + } + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (BadHttpRequestException badHttpRequestException) + { + logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException); + httpContext.Response.StatusCode = badHttpRequestException.StatusCode; + return (false, default); + } + catch (IOException ioException) + { + logOrThrowExceptionHelper.RequestBodyIOException(ioException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + catch (System.Text.Json.JsonException jsonException) + { + logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException); + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + else if (!allowEmpty) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + + return (allowEmpty, default); + } + + } + + file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata + { + public GeneratedAcceptsMetadata(string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(contentTypes); + + ContentTypes = contentTypes; + } + + public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(contentTypes); + + RequestType = type; + ContentTypes = contentTypes; + IsOptional = isOptional; + } + + public IReadOnlyList ContentTypes { get; } + + public Type? RequestType { get; } + + public bool IsOptional { get; } + } + file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata + { + public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes) + { + Type = type; + StatusCode = statusCode; + ContentTypes = contentTypes; + } + + public Type? Type { get; } + + public int StatusCode { get; } + + public IEnumerable ContentTypes { get; } + } + + file static class GeneratedMetadataConstants + { + public static readonly string[] JsonContentType = new [] { "application/json" }; + public static readonly string[] PlaintextContentType = new [] { "text/plain" }; + public static readonly string[] FormFileContentType = new[] { "multipart/form-data" }; + public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; + } + + + file sealed class LogOrThrowExceptionHelper + { + private readonly ILogger? _rdgLogger; + private readonly bool _shouldThrow; + + public LogOrThrowExceptionHelper(IServiceProvider? serviceProvider, RequestDelegateFactoryOptions? options) + { + var loggerFactory = serviceProvider?.GetRequiredService(); + _rdgLogger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator"); + _shouldThrow = options?.ThrowOnBadRequest ?? false; + } + + public void RequestBodyIOException(IOException exception) + { + if (_rdgLogger != null) + { + _requestBodyIOException(_rdgLogger, exception); + } + } + + private static readonly Action _requestBodyIOException = + LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestBodyIOException"), "Reading the request body failed with an IOException."); + + public void InvalidJsonRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as JSON.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidJsonRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidJsonRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(2, "InvalidJsonRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as JSON."); + + public void ParameterBindingFailed(string parameterTypeName, string parameterName, string sourceValue) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to bind parameter \"{0} {1}\" from \"{2}\".", parameterTypeName, parameterName, sourceValue); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _parameterBindingFailed(_rdgLogger, parameterTypeName, parameterName, sourceValue, null); + } + } + + private static readonly Action _parameterBindingFailed = + LoggerMessage.Define(LogLevel.Debug, new EventId(3, "ParameterBindingFailed"), "Failed to bind parameter \"{ParameterType} {ParameterName}\" from \"{SourceValue}\"."); + + public void RequiredParameterNotProvided(string parameterTypeName, string parameterName, string source) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Required parameter \"{0} {1}\" was not provided from {2}.", parameterTypeName, parameterName, source); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _requiredParameterNotProvided(_rdgLogger, parameterTypeName, parameterName, source, null); + } + } + + private static readonly Action _requiredParameterNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(4, "RequiredParameterNotProvided"), "Required parameter \"{ParameterType} {ParameterName}\" was not provided from {Source}."); + + public void ImplicitBodyNotProvided(string parameterName) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Implicit body inferred for parameter \"{0}\" but no body was provided. Did you mean to use a Service instead?", parameterName); + throw new BadHttpRequestException(message); + } + + if (_rdgLogger != null) + { + _implicitBodyNotProvided(_rdgLogger, parameterName, null); + } + } + + private static readonly Action _implicitBodyNotProvided = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, "ImplicitBodyNotProvided"), "Implicit body inferred for parameter \"{ParameterName}\" but no body was provided. Did you mean to use a Service instead?"); + + public void UnexpectedJsonContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported JSON media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedJsonContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedJsonContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(6, "UnexpectedContentType"), "Expected a supported JSON media type but got \"{ContentType}\"."); + + public void UnexpectedNonFormContentType(string? contentType) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported form media type but got \"{0}\".", contentType); + throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType); + } + + if (_rdgLogger != null) + { + _unexpectedNonFormContentType(_rdgLogger, contentType ?? "(none)", null); + } + } + + private static readonly Action _unexpectedNonFormContentType = + LoggerMessage.Define(LogLevel.Debug, new EventId(7, "UnexpectedNonFormContentType"), "Expected a supported form media type but got \"{ContentType}\"."); + + public void InvalidFormRequestBody(string parameterTypeName, string parameterName, Exception exception) + { + if (_shouldThrow) + { + var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as form.", parameterTypeName, parameterName); + throw new BadHttpRequestException(message, exception); + } + + if (_rdgLogger != null) + { + _invalidFormRequestBody(_rdgLogger, parameterTypeName, parameterName, exception); + } + } + + private static readonly Action _invalidFormRequestBody = + LoggerMessage.Define(LogLevel.Debug, new EventId(8, "InvalidFormRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as form."); + } +} diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index ce2e1c9b2db7..1b33de3829b9 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -117,14 +117,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - var serviceProviderIsService = serviceProvider?.GetService(); - var p_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "string[]", "p", serviceProviderIsService); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -141,12 +141,21 @@ namespace Microsoft.AspNetCore.Http.Generated async Task RequestHandler(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrService) - var p_resolveJsonBodyOrServiceResult = await p_JsonBodyOrServiceResolver(httpContext, false); - var p_local = p_resolveJsonBodyOrServiceResult.Item2; - if (!p_resolveJsonBodyOrServiceResult.Item1) + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) { - return; + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } } if (wasParamCheckFailure) @@ -155,26 +164,35 @@ namespace Microsoft.AspNetCore.Http.Generated return; } httpContext.Response.ContentType ??= "text/plain"; - var result = handler(p_local!); + var result = handler(p_local); await httpContext.Response.WriteAsync(result); } async Task RequestHandlerFiltered(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrService) - var p_resolveJsonBodyOrServiceResult = await p_JsonBodyOrServiceResolver(httpContext, false); - var p_local = p_resolveJsonBodyOrServiceResult.Item2; - if (!p_resolveJsonBodyOrServiceResult.Item1) + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) { - return; + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } } if (wasParamCheckFailure) { httpContext.Response.StatusCode = 400; } - var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local!)); + var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local)); await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); } @@ -289,17 +307,6 @@ namespace Microsoft.AspNetCore.Http.Generated return (allowEmpty, default); } - private static Func> ResolveJsonBodyOrService(LogOrThrowExceptionHelper logOrThrowExceptionHelper, string parameterTypeName, string parameterName, IServiceProviderIsService? serviceProviderIsService = null) - { - if (serviceProviderIsService is not null) - { - if (serviceProviderIsService.IsService(typeof(T))) - { - return static (httpContext, isOptional) => new ValueTask<(bool, T?)>((true, httpContext.RequestServices.GetService())); - } - } - return (httpContext, isOptional) => TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, isOptional, parameterTypeName, parameterName, isInferred: true); - } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt index e28928eba33b..a807d19925e3 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt @@ -117,16 +117,16 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - var serviceProviderIsService = serviceProvider?.GetService(); - var p_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "string[]", "p", serviceProviderIsService); var serializerOptions = serviceProvider?.GetService>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions; var jsonTypeInfo = (JsonTypeInfo)serializerOptions.GetTypeInfo(typeof(global::System.Int32)); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -143,12 +143,21 @@ namespace Microsoft.AspNetCore.Http.Generated async Task RequestHandler(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrService) - var p_resolveJsonBodyOrServiceResult = await p_JsonBodyOrServiceResolver(httpContext, false); - var p_local = p_resolveJsonBodyOrServiceResult.Item2; - if (!p_resolveJsonBodyOrServiceResult.Item1) + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) { - return; + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } } if (wasParamCheckFailure) @@ -157,26 +166,35 @@ namespace Microsoft.AspNetCore.Http.Generated return; } httpContext.Response.ContentType ??= "application/json"; - var result = handler(p_local!); + var result = handler(p_local); await httpContext.Response.WriteAsJsonAsync(result, jsonTypeInfo); } async Task RequestHandlerFiltered(HttpContext httpContext) { var wasParamCheckFailure = false; - // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrService) - var p_resolveJsonBodyOrServiceResult = await p_JsonBodyOrServiceResolver(httpContext, false); - var p_local = p_resolveJsonBodyOrServiceResult.Item2; - if (!p_resolveJsonBodyOrServiceResult.Item1) + // Endpoint Parameter: p (Type = string[], IsOptional = False, IsParsable = False, IsArray = True, Source = JsonBodyOrQuery) + global::System.String[] p_local = null!; + if (options.DisableInferBodyFromParameters) { - return; + var p_raw = httpContext.Request.Query["p"]; + p_local = p_raw!; + } + else + { + var p_resolveBodyResult = await GeneratedRouteBuilderExtensionsCore.TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, false, "string[]", "p"); + p_local = p_resolveBodyResult.Item2!; + if (!p_resolveBodyResult.Item1) + { + return; + } } if (wasParamCheckFailure) { httpContext.Response.StatusCode = 400; } - var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local!)); + var result = await filteredInvocation(EndpointFilterInvocationContext.Create(httpContext, p_local)); await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); } @@ -291,17 +309,6 @@ namespace Microsoft.AspNetCore.Http.Generated return (allowEmpty, default); } - private static Func> ResolveJsonBodyOrService(LogOrThrowExceptionHelper logOrThrowExceptionHelper, string parameterTypeName, string parameterName, IServiceProviderIsService? serviceProviderIsService = null) - { - if (serviceProviderIsService is not null) - { - if (serviceProviderIsService.IsService(typeof(T))) - { - return static (httpContext, isOptional) => new ValueTask<(bool, T?)>((true, httpContext.RequestServices.GetService())); - } - } - return (httpContext, isOptional) => TryResolveBodyAsync(httpContext, logOrThrowExceptionHelper, isOptional, parameterTypeName, parameterName, isInferred: true); - } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt index 71d6b29f0e3d..fe47d8a379f1 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt @@ -134,10 +134,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -192,10 +194,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -250,10 +254,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func>)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(async ic => { @@ -309,10 +315,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func>)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(async ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt index 085aba858bdb..a2ce06da8c40 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt @@ -134,10 +134,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -196,10 +198,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -258,10 +262,12 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt index f0a611c81299..3c742a4127ae 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt @@ -104,12 +104,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt index 993bbb92b028..9b3346417b91 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt @@ -163,12 +163,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -274,13 +276,15 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - var Value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("Value", options?.RouteParameterNames); + var Value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("Value", options.RouteParameterNames); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -378,12 +382,14 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -455,6 +461,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Func)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -462,7 +470,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serviceProviderIsService = serviceProvider?.GetService(); var Todo_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "TodoStruct", "Todo", serviceProviderIsService); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { @@ -545,6 +553,8 @@ namespace Microsoft.AspNetCore.Http.Generated { Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); + Debug.Assert(options.EndpointBuilder.ApplicationServices != null, "ApplicationServices not found."); + Debug.Assert(options.EndpointBuilder.FilterFactories != null, "FilterFactories not found."); var handler = (System.Action)del; EndpointFilterDelegate? filteredInvocation = null; var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices; @@ -552,7 +562,7 @@ namespace Microsoft.AspNetCore.Http.Generated var serviceProviderIsService = serviceProvider?.GetService(); var Value_JsonBodyOrServiceResolver = ResolveJsonBodyOrService(logOrThrowExceptionHelper, "AddsCustomParameterMetadataAsProperty", "Value", serviceProviderIsService); - if (options?.EndpointBuilder?.FilterFactories.Count > 0) + if (options.EndpointBuilder.FilterFactories.Count > 0) { filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic => { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs index 509c782b24b3..41a2745b728d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Http.Generators.Tests; public abstract class RequestDelegateCreationTestBase : LoggedTest { // Change this to true and run tests in development to regenerate baseline files. - public bool RegenerateBaselines => false; + public bool RegenerateBaselines ; protected abstract bool IsGeneratorEnabled { get; } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Arrays.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Arrays.cs index 551f54186679..f3f2715b52f7 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Arrays.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Arrays.cs @@ -373,4 +373,111 @@ public async Task MapPost_WithArrayQueryString_AndBody_ShouldUseBody() await VerifyAgainstBaselineUsingFile(compilation); } + [Fact] + public async Task MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody() + { + var (results, compilation) = await RunGeneratorAsync(""" +app.MapMethods("/hello", new [] { "POST" }, (string[] p) => p[0]); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + VerifyStaticEndpointModel(results, endpointModel => + { + Assert.Equal("MapMethods", endpointModel.HttpMethod); + }); + + var httpContext = CreateHttpContext(); + httpContext.Features.Set(new RequestBodyDetectionFeature(true)); + httpContext.Request.Headers["Content-Type"] = "application/json"; + var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new string[] { "ValueFromBody" }); + var stream = new MemoryStream(requestBodyBytes); + httpContext.Request.Body = stream; + httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture); + httpContext.Request.QueryString = new QueryString("?p=ValueFromQueryString"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "ValueFromBody"); + await VerifyAgainstBaselineUsingFile(compilation); + } + + [Fact] + public async Task MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString() + { + var (results, compilation) = await RunGeneratorAsync(""" +app.MapMethods("/hello", new [] { "GET" }, (string[] p) => p[0]); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + VerifyStaticEndpointModel(results, endpointModel => + { + Assert.Equal("MapMethods", endpointModel.HttpMethod); + }); + + var httpContext = CreateHttpContext(); + httpContext.Features.Set(new RequestBodyDetectionFeature(true)); + httpContext.Request.Headers["Content-Type"] = "application/json"; + var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new string[] { "ValueFromBody" }); + var stream = new MemoryStream(requestBodyBytes); + httpContext.Request.Body = stream; + httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture); + httpContext.Request.QueryString = new QueryString("?p=ValueFromQueryString"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "ValueFromQueryString"); + await VerifyAgainstBaselineUsingFile(compilation); + } + + [Fact] + public async Task MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString() + { + var (results, compilation) = await RunGeneratorAsync(""" +app.MapMethods("/hello", new [] { "POST", "GET" }, (string[] p) => p[0]); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + VerifyStaticEndpointModel(results, endpointModel => + { + Assert.Equal("MapMethods", endpointModel.HttpMethod); + }); + + var httpContext = CreateHttpContext(); + httpContext.Features.Set(new RequestBodyDetectionFeature(true)); + httpContext.Request.Headers["Content-Type"] = "application/json"; + var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new string[] { "ValueFromBody" }); + var stream = new MemoryStream(requestBodyBytes); + httpContext.Request.Body = stream; + httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture); + httpContext.Request.QueryString = new QueryString("?p=ValueFromQueryString"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "ValueFromQueryString"); + await VerifyAgainstBaselineUsingFile(compilation); + } + + [Fact] + public async Task MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody() + { + var (results, compilation) = await RunGeneratorAsync(""" +app.MapMethods("/hello", new [] { "POST", "PUT" }, (string[] p) => p[0]); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + VerifyStaticEndpointModel(results, endpointModel => + { + Assert.Equal("MapMethods", endpointModel.HttpMethod); + }); + + var httpContext = CreateHttpContext(); + httpContext.Features.Set(new RequestBodyDetectionFeature(true)); + httpContext.Request.Headers["Content-Type"] = "application/json"; + var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new string[] { "ValueFromBody" }); + var stream = new MemoryStream(requestBodyBytes); + httpContext.Request.Body = stream; + httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture); + httpContext.Request.QueryString = new QueryString("?p=ValueFromQueryString"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "ValueFromBody"); + await VerifyAgainstBaselineUsingFile(compilation); + } }