diff --git a/.azure/pipelines/ci-public.yml b/.azure/pipelines/ci-public.yml index 124806b65edd..2f506299bbb5 100644 --- a/.azure/pipelines/ci-public.yml +++ b/.azure/pipelines/ci-public.yml @@ -556,9 +556,6 @@ stages: beforeBuild: - powershell: "& ./src/Servers/IIS/tools/UpdateIISExpressCertificate.ps1; & ./src/Servers/IIS/tools/update_schema.ps1" displayName: Setup IISExpress test certificates and schema - afterBuild: - - powershell: ./eng/scripts/CompareMinifiedJsFiles.ps1 - displayName: Check for changes in generated minified .js files artifacts: - name: Windows_Test_Logs_Attempt_$(System.JobAttempt) path: artifacts/log/ diff --git a/AspNetCore.sln b/AspNetCore.sln index a9c2857c4182..aba0325d0351 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1674,8 +1674,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResultsOfTGenerator", "src\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenApi", "OpenApi", "{2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OpenApi.Tests", "src\OpenApi\test\Microsoft.AspNetCore.OpenApi.Tests.csproj", "{3AEFB466-6310-4F3F-923F-9154224E3629}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OpenApi", "src\OpenApi\src\Microsoft.AspNetCore.OpenApi.csproj", "{EFC8EA45-572D-4D8D-A597-9045A2D8EC40}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.RateLimiting", "src\Middleware\RateLimiting\src\Microsoft.AspNetCore.RateLimiting.csproj", "{8EE73488-2B92-42BD-96C9-0DD65405C828}" @@ -1818,6 +1816,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetDocumentSample", "src\To EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorUnitedApp.Client", "src\Components\Samples\BlazorUnitedApp.Client\BlazorUnitedApp.Client.csproj", "{757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B32FF7A7-9CB3-4DCD-AE97-3B2594DB9DAC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.Tests", "src\OpenApi\test\Microsoft.AspNetCore.OpenApi.Tests\Microsoft.AspNetCore.OpenApi.Tests.csproj", "{B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -10147,22 +10149,6 @@ Global {9716D0D0-2251-44DD-8596-67D253EEF41C}.Release|x64.Build.0 = Release|Any CPU {9716D0D0-2251-44DD-8596-67D253EEF41C}.Release|x86.ActiveCfg = Release|Any CPU {9716D0D0-2251-44DD-8596-67D253EEF41C}.Release|x86.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|arm64.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|arm64.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x64.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x64.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x86.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x86.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|Any CPU.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|arm64.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|arm64.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x64.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x64.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x86.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x86.Build.0 = Release|Any CPU {EFC8EA45-572D-4D8D-A597-9045A2D8EC40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EFC8EA45-572D-4D8D-A597-9045A2D8EC40}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFC8EA45-572D-4D8D-A597-9045A2D8EC40}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -10995,6 +10981,22 @@ Global {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x64.Build.0 = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.ActiveCfg = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|arm64.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|arm64.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x64.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x86.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|Any CPU.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|arm64.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|arm64.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x64.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x64.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x86.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -11824,7 +11826,6 @@ Global {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7} = {AA5ABFBC-177C-421E-B743-005E0FD1248B} {9716D0D0-2251-44DD-8596-67D253EEF41C} = {323C3EB6-1D15-4B3D-918D-699D7F64DED9} {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} = {017429CC-C5FB-48B4-9C46-034E29EE2F06} - {3AEFB466-6310-4F3F-923F-9154224E3629} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} {EFC8EA45-572D-4D8D-A597-9045A2D8EC40} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} {8EE73488-2B92-42BD-96C9-0DD65405C828} = {1D865E78-7A66-4CA9-92EE-2B350E45281F} {41FF4F96-98D2-4482-A2A7-4B179E80D285} = {1D865E78-7A66-4CA9-92EE-2B350E45281F} @@ -11893,6 +11894,8 @@ Global {6A19D94D-2BC6-4198-BE2E-342688FDBA4B} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {D8F7091E-A2D1-4E81-BA7C-97EAE392D683} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63} = {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} + {B32FF7A7-9CB3-4DCD-AE97-3B2594DB9DAC} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7} = {B32FF7A7-9CB3-4DCD-AE97-3B2594DB9DAC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/eng/RequiresDelayedBuildProjects.props b/eng/RequiresDelayedBuildProjects.props index 3c1626ba53bf..1a771e0d5e74 100644 --- a/eng/RequiresDelayedBuildProjects.props +++ b/eng/RequiresDelayedBuildProjects.props @@ -20,6 +20,7 @@ + diff --git a/eng/TrimmableProjects.props b/eng/TrimmableProjects.props index 6ba207ac1852..199d65af1b49 100644 --- a/eng/TrimmableProjects.props +++ b/eng/TrimmableProjects.props @@ -85,6 +85,9 @@ + + + diff --git a/eng/testing/linker/SupportFiles/Directory.Build.targets b/eng/testing/linker/SupportFiles/Directory.Build.targets index 8d7674b8923a..0b4c65ac90c2 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.targets +++ b/eng/testing/linker/SupportFiles/Directory.Build.targets @@ -75,6 +75,8 @@ + + diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 8efde8d4b648..1affe37e05ef 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -11,6 +11,11 @@ 99.9 <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) + $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated + + false + + $(NoWarn);IL2104 {AdditionalProperties} @@ -21,5 +26,20 @@ {AdditionalProjectReferences} - + + + + + + + + + true + true + + + + diff --git a/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs new file mode 100644 index 000000000000..1ca1c6448ec2 --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Exposes metadata about the parameter binding details associated with a parameter +/// in the endpoints handler. +/// +/// +/// This metadata is injected by the RequestDelegateFactory and RequestDelegateGenerator components +/// and is primarily intended for consumption by the EndpointMetadataApiDescriptionProvider in +/// ApiExplorer. +/// +public interface IParameterBindingMetadata +{ + /// + /// The name of the parameter. + /// + string Name { get; } + + /// + /// is the parameter is associated with a type that implements or exposes a TryParse method. + /// + bool HasTryParse { get; } + + /// + /// if the parameter is associated with a type that implements a BindAsync method. + /// + bool HasBindAsync { get; } + + /// + /// The associated with the parameter. + /// + ParameterInfo ParameterInfo { get; } + + /// + /// if the parameter is optional. + /// + bool IsOptional { get; } +} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index f31484140679..0d2aab25f97d 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -5,3 +5,9 @@ Microsoft.AspNetCore.Http.HostString.HostString(string? value) -> void Microsoft.AspNetCore.Http.HostString.Value.get -> string? Microsoft.AspNetCore.Http.HttpValidationProblemDetails.HttpValidationProblemDetails(System.Collections.Generic.IEnumerable>! errors) -> void Microsoft.AspNetCore.Http.Metadata.IDisableHttpMetricsMetadata +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.HasBindAsync.get -> bool +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.HasTryParse.get -> bool +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.IsOptional.get -> bool +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.Name.get -> string! +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.ParameterInfo.get -> System.Reflection.ParameterInfo! diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs b/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs index ac8bbdd3a175..f4e2ad0e8e33 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs @@ -5,14 +5,12 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Text; using Microsoft.AspNetCore.Analyzers.Infrastructure; using Microsoft.AspNetCore.App.Analyzers.Infrastructure; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Operations; namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator; @@ -243,6 +241,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) 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); + var requiresParameterBindingMetadataClass = endpoints.Any(endpoint => endpoint.EmitterContext.RequiresParameterBindingMetadataClass); using var stringWriter = new StringWriter(CultureInfo.InvariantCulture); using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0); @@ -262,6 +261,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) codeWriter.WriteLine(RequestDelegateGeneratorSources.PropertyAsParameterInfoClass); } + if (requiresParameterBindingMetadataClass) + { + codeWriter.WriteLine(RequestDelegateGeneratorSources.ParameterBindingMetadataClass); + } + return stringWriter.ToString(); }); diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs index b3e629a5cdee..d7c7a480446c 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs @@ -449,6 +449,36 @@ public override bool IsDefined(Type attributeType, bool inherit) } """; + public static string ParameterBindingMetadataClass = $$""" + {{GeneratedCodeAttribute}} + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } +"""; + public static string AntiforgeryMetadataType = """ file sealed class AntiforgeryMetadata : IAntiforgeryMetadata { diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs index 43c0906df211..ce950dc54bfe 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs @@ -12,6 +12,7 @@ internal sealed class EmitterContext public bool HasBindAsync { get; set; } public bool HasParsable { get; set; } public bool RequiresPropertyAsParameterInfo { get; set; } + public bool RequiresParameterBindingMetadataClass { get; set; } public bool RequiresLoggingHelper { get; set; } public bool HasEndpointMetadataProvider { get; set; } public bool HasEndpointParameterMetadataProvider { get; set; } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs index 4854c72937d2..1eca598bc655 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs @@ -47,7 +47,7 @@ private EndpointParameter(Endpoint endpoint, IPropertySymbol property, IParamete PropertyAsParameterInfoConstruction = parameter is not null ? $"new PropertyAsParameterInfo({(IsOptional ? "true" : "false")}, {propertyInfo}, {parameter.GetParameterInfoFromConstructorCode()})" : $"new PropertyAsParameterInfo({(IsOptional ? "true" : "false")}, {propertyInfo})"; - endpoint.EmitterContext.RequiresPropertyAsParameterInfo = IsProperty && IsEndpointParameterMetadataProvider; + endpoint.EmitterContext.RequiresPropertyAsParameterInfo = IsProperty; ProcessEndpointParameterSource(endpoint, property, attributeBuilder.ToImmutable(), wellKnownTypes); } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs index 7a206872d797..bd22c645025b 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; @@ -199,21 +200,26 @@ public static void EmitFilteredRequestHandler(this Endpoint endpoint, CodeWriter private static void EmitBuiltinResponseTypeMetadata(this Endpoint endpoint, CodeWriter codeWriter) { - if (endpoint.Response is not { } response || response.ResponseType is not { } responseType) + if (endpoint.Response is not { } response) { return; } - if (response.HasNoResponse || response.IsIResult) + if (!endpoint.Response.IsAwaitable && (response.HasNoResponse || response.IsIResult)) { return; } - if (responseType.SpecialType == SpecialType.System_String) + endpoint.EmitterContext.HasResponseMetadata = true; + if (response.ResponseType?.SpecialType == SpecialType.System_String) { - codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType));"); + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType));"); } - else + else if (response.IsAwaitable && response.ResponseType == null) + { + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(void), contentTypes: GeneratedMetadataConstants.PlaintextContentType));"); + } + else if (response.ResponseType is { } responseType) { codeWriter.WriteLine($$"""options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof({{responseType.ToDisplayString(EmitterConstants.DisplayFormatWithoutNullability)}}), contentTypes: GeneratedMetadataConstants.JsonContentType));"""); } @@ -358,9 +364,39 @@ public static void EmitAcceptsMetadata(this Endpoint endpoint, CodeWriter codeWr } } + public static void EmitParameterBindingMetadata(this Endpoint endpoint, CodeWriter codeWriter) + { + foreach (var parameter in endpoint.Parameters) + { + endpoint.EmitterContext.RequiresParameterBindingMetadataClass = true; + if (parameter.EndpointParameters is not null) + { + foreach (var propertyAsParameter in parameter.EndpointParameters) + { + EmitParameterBindingMetadataForParameter(propertyAsParameter, codeWriter); + } + } + else + { + EmitParameterBindingMetadataForParameter(parameter, codeWriter); + } + } + + static void EmitParameterBindingMetadataForParameter(EndpointParameter parameter, CodeWriter codeWriter) + { + var parameterName = SymbolDisplay.FormatLiteral(parameter.SymbolName, true); + var parameterInfo = parameter.IsProperty ? parameter.PropertyAsParameterInfoConstruction : $"methodInfo.GetParameters()[{parameter.Ordinal}]"; + var hasTryParse = parameter.IsParsable ? "true" : "false"; + var hasBindAsync = parameter.Source == EndpointParameterSource.BindAsync ? "true" : "false"; + var isOptional = parameter.IsOptional ? "true" : "false"; + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata({parameterName}, {parameterInfo}, hasTryParse: {hasTryParse}, hasBindAsync: {hasBindAsync}, isOptional: {isOptional}));"); + } + } + public static void EmitEndpointMetadataPopulation(this Endpoint endpoint, CodeWriter codeWriter) { endpoint.EmitAcceptsMetadata(codeWriter); + endpoint.EmitParameterBindingMetadata(codeWriter); endpoint.EmitBuiltinResponseTypeMetadata(codeWriter); endpoint.EmitCallsToMetadataProvidersForParameters(codeWriter); endpoint.EmitCallToMetadataProviderForResponse(codeWriter); diff --git a/src/Http/Http.Extensions/src/ParameterBindingMetadata.cs b/src/Http/Http.Extensions/src/ParameterBindingMetadata.cs new file mode 100644 index 000000000000..6bc15b868aef --- /dev/null +++ b/src/Http/Http.Extensions/src/ParameterBindingMetadata.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; + +namespace Microsoft.AspNetCore.Http.Metadata; + +internal sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : IParameterBindingMetadata +{ + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; +} diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index c2e2b1ea0d28..f30dbe63ca10 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -38,8 +38,6 @@ namespace Microsoft.AspNetCore.Http; [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static partial class RequestDelegateFactory { - private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); - private static readonly MethodInfo ExecuteTaskWithEmptyResultMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTaskWithEmptyResult), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo ExecuteValueTaskWithEmptyResultMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteValueTaskWithEmptyResult), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo ExecuteTaskOfTMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTaskOfT), BindingFlags.NonPublic | BindingFlags.Static)!; @@ -648,8 +646,18 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Request for (var i = 0; i < parameters.Length; i++) { - args[i] = CreateArgument(parameters[i], factoryContext); + args[i] = CreateArgument(parameters[i], factoryContext, out var hasTryParse, out var hasBindAsync, out var isAsParameters); + if (!isAsParameters) + { + factoryContext.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata( + name: parameters[i].Name!, + parameterInfo: parameters[i], + hasTryParse: hasTryParse, + hasBindAsync: hasBindAsync, + isOptional: IsOptionalParameter(parameters[i], factoryContext) + )); + } factoryContext.ArgumentTypes[i] = parameters[i].ParameterType; factoryContext.BoxedArgs[i] = Expression.Convert(args[i], typeof(object)); } @@ -675,8 +683,11 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Request return args; } - private static Expression CreateArgument(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext) + private static Expression CreateArgument(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext, out bool hasTryParse, out bool hasBindAsync, out bool isAsParameters) { + hasTryParse = false; + hasBindAsync = false; + isAsParameters = false; if (parameter.Name is null) { throw new InvalidOperationException($"Encountered a parameter of type '{parameter.ParameterType}' without a name. Parameters must have a name."); @@ -770,8 +781,9 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat var useSimpleBinding = parameter.ParameterType == typeof(string) || parameter.ParameterType == typeof(StringValues) || parameter.ParameterType == typeof(StringValues?) || - ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)); + ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType) || + (parameter.ParameterType.IsArray && ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType.GetElementType()!)); + hasTryParse = useSimpleBinding; return useSimpleBinding ? BindParameterFromFormItem(parameter, formAttribute.Name ?? parameter.Name, factoryContext) : BindComplexParameterFromFormItem(parameter, string.IsNullOrEmpty(formAttribute.Name) ? parameter.Name : formAttribute.Name, factoryContext); @@ -797,6 +809,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat } else if (parameterCustomAttributes.OfType().Any()) { + isAsParameters = true; if (parameter is PropertyAsParameterInfo) { throw new NotSupportedException( @@ -845,12 +858,14 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat { return RequestPipeReaderExpr; } - else if (ParameterBindingMethodCache.HasBindAsyncMethod(parameter)) + else if (ParameterBindingMethodCache.Instance.HasBindAsyncMethod(parameter)) { + hasBindAsync = true; return BindParameterFromBindAsync(parameter, factoryContext); } - else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) + else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType)) { + hasTryParse = true; // 1. We bind from route values only, if route parameters are non-null and the parameter name is in that set. // 2. We bind from query only, if route parameters are non-null and the parameter name is NOT in that set. // 3. Otherwise, we fallback to route or query if route parameters is null (it means we don't know what route parameters are defined). This case only happens @@ -878,10 +893,10 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat parameter.ParameterType == typeof(string[]) || parameter.ParameterType == typeof(StringValues) || parameter.ParameterType == typeof(StringValues?) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) + (parameter.ParameterType.IsArray && ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) { // We only infer parameter types if you have an array of TryParsables/string[]/StringValues/StringValues?, and DisableInferredFromBody is true - + hasTryParse = true; factoryContext.TrackedParameters.Add(parameter.Name, RequestDelegateFactoryConstants.QueryStringParameter); return BindParameterFromProperty(parameter, QueryExpr, QueryIndexerProperty, parameter.Name, factoryContext, "query string"); } @@ -1009,20 +1024,26 @@ private static void PopulateBuiltInResponseTypeMetadata(Type returnType, Endpoin throw GetUnsupportedReturnTypeException(returnType); } + var isAwaitable = false; if (CoercedAwaitableInfo.IsTypeAwaitable(returnType, out var coercedAwaitableInfo)) { returnType = coercedAwaitableInfo.AwaitableInfo.ResultType; + isAwaitable = true; } // Skip void returns and IResults. IResults might implement IEndpointMetadataProvider but otherwise we don't know what it might do. - if (returnType == typeof(void) || typeof(IResult).IsAssignableFrom(returnType)) + if (!isAwaitable && (returnType == typeof(void) || typeof(IResult).IsAssignableFrom(returnType))) { return; } if (returnType == typeof(string)) { - builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: null, statusCode: 200, PlaintextContentType)); + builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: typeof(string), statusCode: 200, PlaintextContentType)); + } + else if (returnType == typeof(void)) + { + builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(returnType, statusCode: 200, PlaintextContentType)); } else { @@ -1542,7 +1563,7 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, R } var argumentExpression = Expression.Variable(parameter.ParameterType, $"{parameter.Name}_local"); - var (constructor, parameters) = ParameterBindingMethodCache.FindConstructor(parameterType); + var (constructor, parameters) = ParameterBindingMethodCache.Instance.FindConstructor(parameterType); Expression initExpression; @@ -1556,8 +1577,10 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, R { var parameterInfo = new PropertyAsParameterInfo(parameters[i].PropertyInfo, parameters[i].ParameterInfo, factoryContext.NullabilityContext); - constructorArguments[i] = CreateArgument(parameterInfo, factoryContext); + Debug.Assert(parameterInfo.Name != null, "Parameter name must be set for parameters resolved from properties."); + constructorArguments[i] = CreateArgument(parameterInfo, factoryContext, out var hasTryParse, out var hasBindAsync, out var _); factoryContext.Parameters.Add(parameterInfo); + factoryContext.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata(parameterInfo.Name, parameterInfo, hasTryParse: hasTryParse, hasBindAsync: hasBindAsync, isOptional: parameterInfo.IsOptional)); } initExpression = Expression.New(constructor, constructorArguments); @@ -1579,8 +1602,10 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, R if (properties[i].CanWrite && properties[i].GetSetMethod(nonPublic: false) != null) { var parameterInfo = new PropertyAsParameterInfo(properties[i], factoryContext.NullabilityContext); - bindings.Add(Expression.Bind(properties[i], CreateArgument(parameterInfo, factoryContext))); + Debug.Assert(parameterInfo.Name != null, "Parameter name must be set for parameters resolved from properties."); + bindings.Add(Expression.Bind(properties[i], CreateArgument(parameterInfo, factoryContext, out var hasTryParse, out var hasBindAsync, out var _))); factoryContext.Parameters.Add(parameterInfo); + factoryContext.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata(parameterInfo.Name, parameterInfo, hasTryParse: hasTryParse, hasBindAsync: hasBindAsync, isOptional: parameterInfo.IsOptional)); } } @@ -1649,7 +1674,7 @@ private static Expression BindParameterFromValue(ParameterInfo parameter, Expres var isNotNullable = underlyingNullableType is null; var nonNullableParameterType = underlyingNullableType ?? targetParseType; - var tryParseMethodCall = ParameterBindingMethodCache.FindTryParseMethod(nonNullableParameterType); + var tryParseMethodCall = ParameterBindingMethodCache.Instance.FindTryParseMethod(nonNullableParameterType); if (tryParseMethodCall is null) { @@ -1947,7 +1972,7 @@ private static Expression BindParameterFromBindAsync(ParameterInfo parameter, Re var isOptional = IsOptionalParameter(parameter, factoryContext); // Get the BindAsync method for the type. - var bindAsyncMethod = ParameterBindingMethodCache.FindBindAsyncMethod(parameter); + var bindAsyncMethod = ParameterBindingMethodCache.Instance.FindBindAsyncMethod(parameter); // We know BindAsync exists because there's no way to opt-in without defining the method on the type. Debug.Assert(bindAsyncMethod.Expression is not null); diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 452eeb6c0674..2afff2e67bd7 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -2548,7 +2548,7 @@ public void Create_AddPlaintextResponseType_AsMetadata() var responseMetadata = Assert.IsAssignableFrom(Assert.Single(result.EndpointMetadata)); Assert.Equal("text/plain", Assert.Single(responseMetadata.ContentTypes)); - Assert.Null(responseMetadata.Type); + Assert.Equal(typeof(string), responseMetadata.Type); } [Fact] @@ -2567,6 +2567,7 @@ public void Create_DoesNotAddAnythingBefore_ThePassedInEndpointMetadata() // but we just specified our CustomEndpointMetadata in this test. Assert.Collection(result.EndpointMetadata, m => Assert.Same(customMetadata, m), + m => Assert.True(m is IParameterBindingMetadata { HasBindAsync : true }), m => Assert.True(m is ParameterNameMetadata { Name: "param1" }), m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Parameter })); } @@ -2704,8 +2705,9 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromTaskWrappedReturnTypes // Assert Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller }); - // Expecting '1' because only initial metadata will be in the metadata list when this metadata item is added - Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 }); + Assert.Contains(result.EndpointMetadata, m => m is ProducesResponseTypeMetadata { Type: { } type } && type == typeof(CountsDefaultEndpointMetadataResult)); + // Expecting the custom metadata and the implicit metadata associated with a Task-based return type to be inserted + Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 2 }); } [Fact] @@ -2726,8 +2728,9 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromValueTaskWrappedReturn // Assert Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller }); - // Expecting '1' because only initial metadata will be in the metadata list when this metadata item is added - Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 }); + Assert.Contains(result.EndpointMetadata, m => m is ProducesResponseTypeMetadata { Type: { } type } && type == typeof(CountsDefaultEndpointMetadataResult)); + // Expecting the custom metadata nad hte implicit metadata associated with a Task-based return type to be inserted + Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 2 }); } [Fact] @@ -2748,8 +2751,9 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromFSharpAsyncWrappedRetu // Assert Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller }); + Assert.Contains(result.EndpointMetadata, m => m is IProducesResponseTypeMetadata { Type: { } type } && type == typeof(CountsDefaultEndpointMetadataResult)); // Expecting '1' because only initial metadata will be in the metadata list when this metadata item is added - Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 }); + Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 2 }); } [Fact] @@ -2818,6 +2822,8 @@ public void Create_CombinesAllMetadata_InCorrectOrder() m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Caller }), // Inferred AcceptsMetadata from RDF for complex type m => Assert.True(m is AcceptsMetadata am && am.RequestType == typeof(AddsCustomParameterMetadata)), + // Inferred ParameterBinding metadata + m => Assert.True(m is IParameterBindingMetadata { Name: "param1" }), // Inferred ProducesResopnseTypeMetadata from RDF for complex type m => Assert.Equal(typeof(CountsDefaultEndpointMetadataPoco), ((IProducesResponseTypeMetadata)m).Type), // Metadata provided by parameters implementing IEndpointParameterMetadataProvider @@ -2825,7 +2831,7 @@ public void Create_CombinesAllMetadata_InCorrectOrder() // Metadata provided by parameters implementing IEndpointMetadataProvider m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Parameter }), // Metadata provided by return type implementing IEndpointMetadataProvider - m => Assert.True(m is MetadataCountMetadata { Count: 5 })); + m => Assert.True(m is MetadataCountMetadata { Count: 6 })); } [Fact] @@ -2878,7 +2884,7 @@ public void Create_DoesNotInferMetadata_GivenManuallyConstructedMetadataResult() var result = RequestDelegateFactory.Create(@delegate, options, metadataResult); // Assert - Assert.Empty(result.EndpointMetadata); + Assert.Contains(result.EndpointMetadata, m => m is IParameterBindingMetadata { Name: "param1" }); Assert.Same(options.EndpointBuilder.Metadata, result.EndpointMetadata); // Make extra sure things are running as expected, as this non-InferMetadata path is no longer exercised by RouteEndpointDataSource, diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt index 4ba6654a262e..ae38db998901 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt @@ -69,7 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt index 81d768e52b43..b17d8f132c15 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt @@ -70,7 +70,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("param", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -180,7 +181,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("param", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -368,6 +370,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 e584608eb59d..cd074b2550d4 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 @@ -69,7 +69,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -180,7 +182,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -277,7 +280,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -376,7 +381,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -473,7 +479,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -584,7 +592,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -681,7 +690,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -780,7 +791,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -877,7 +889,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -987,7 +1001,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1083,7 +1098,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1181,7 +1198,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1277,7 +1295,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1388,7 +1408,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1485,7 +1506,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1595,7 +1618,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1691,7 +1715,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1802,7 +1828,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1899,7 +1926,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -2009,7 +2038,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -2184,6 +2214,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 c353cec04fe2..e0820c222878 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 @@ -70,6 +70,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo), isOptional: false, contentTypes: GeneratedMetadataConstants.JsonContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("todo", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); PopulateMetadataForEndpoint>(methodInfo, options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -176,6 +177,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo), isOptional: true, contentTypes: GeneratedMetadataConstants.JsonContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("todo", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); PopulateMetadataForEndpoint>(methodInfo, options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -417,6 +419,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 43c52222ed32..9c14ef7fc2a3 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -266,6 +267,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 561816f4614b..338870404d4f 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -238,6 +239,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 c364250c27de..5c7cdc3d5f31 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -238,6 +239,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 59a8d8116b8c..7be60ca31fb8 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -266,6 +267,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 225d3564d15d..d1747211273c 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -237,6 +238,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 215688d0e1e7..2a4903675562 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -237,6 +238,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 ba54b0c4ad79..0d43f3521412 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 @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -167,7 +168,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -265,7 +267,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svcs", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -441,6 +445,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 4c9760e42208..ec49a917a460 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 @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("queryValue", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -181,7 +182,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("headerValue", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -293,7 +295,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("routeValue", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -414,7 +417,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("value", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -607,6 +611,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 1f4f87e9afca..744cb1967877 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 @@ -69,6 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -273,6 +274,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 c059050111ec..05f5de4967e7 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 @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 c059050111ec..05f5de4967e7 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 @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 c059050111ec..05f5de4967e7 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 @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 5b5a107e475e..fde7b721a577 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 @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 779efb616d06..7866a45bf43a 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 @@ -85,7 +85,9 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("todo", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -351,6 +353,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 add8e79a859a..ef673881c510 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 @@ -69,7 +69,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("req", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("res", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -240,6 +242,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 b76324c9d0f8..6dcece0634d8 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 @@ -69,7 +69,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p1", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p2", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -273,6 +275,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 4ba6654a262e..ae38db998901 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 @@ -69,7 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => 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 4ba6654a262e..ae38db998901 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 @@ -69,7 +69,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => 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 8a4e1c8411b1..e11a4e1978b3 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 @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -273,6 +274,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 4c46221e27a9..00ed02cdf437 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 @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -273,6 +274,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 d3a31800ab3f..9d8c1c5ec159 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 @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -245,6 +246,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 9ce50bcba1fc..cb8f45469fab 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 @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("x", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); var parameterInfos = methodInfo.GetParameters(); var x_ParameterInfo = parameterInfos[0]; PopulateMetadataForParameter(x_ParameterInfo, options.EndpointBuilder); @@ -340,6 +341,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 index a4fc77ad1227..1620b5412616 100644 --- 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 @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 index a4fc77ad1227..1620b5412616 100644 --- 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 @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 index a4fc77ad1227..1620b5412616 100644 --- 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 @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 index a4fc77ad1227..1620b5412616 100644 --- 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 @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 598d3543f866..7ebdfa4be28b 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 @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 1b03fbb80eab..8444c64d3afa 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 @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 484aaefc7f3c..79567d9d3a09 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 @@ -70,7 +70,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -161,7 +161,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -255,7 +255,7 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => 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 17fa1194e78a..5c5b767c4993 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 @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("req", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -164,7 +165,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("res", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -259,7 +261,9 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("req", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("res", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -430,6 +434,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 a093bce3d48c..bb30424cb7ff 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt @@ -71,6 +71,11 @@ namespace Microsoft.AspNetCore.Http.Generated options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(AntiforgeryMetadata.ValidationRequired); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.FormFileContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("file", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("fileCollection", methodInfo.GetParameters()[2], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("collection", methodInfo.GetParameters()[3], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("tryParseRecord", methodInfo.GetParameters()[4], hasTryParse: true, hasBindAsync: false, isOptional: false)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -387,6 +392,33 @@ file sealed class AntiforgeryMetadata : IAntiforgeryMetadata public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt index 746ccff2f381..c5ba9a893684 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt @@ -69,7 +69,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("name", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -182,7 +183,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("age", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -393,6 +395,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt index de4a2f087ea0..4c987b44f6b0 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt @@ -71,7 +71,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("name", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -264,6 +265,33 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper 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 b26c613dc854..30b4541a6f49 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt @@ -72,6 +72,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(int) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(true, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(int) })?.GetParameters()[1]), hasTryParse: true, hasBindAsync: false, isOptional: true)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -208,6 +210,8 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct)!.GetProperty("HttpContext")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct)!.GetProperty("Value")!), hasTryParse: true, hasBindAsync: false, isOptional: false)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -337,6 +341,10 @@ 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 System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("User", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("User")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Request", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("Request")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[2]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Response", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("Response")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[3]), hasTryParse: false, hasBindAsync: false, isOptional: false)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -438,7 +446,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Todo", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody)!.GetProperty("Todo")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -557,6 +567,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); var parameterInfos = methodInfo.GetParameters(); var Value_ParameterInfo = new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[1]); PopulateMetadataForParameter(Value_ParameterInfo, options.EndpointBuilder); @@ -671,7 +683,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Todo", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterRecordStructWithJsonBodyOrService)!.GetProperty("Todo")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Service", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterRecordStructWithJsonBodyOrService)!.GetProperty("Service")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1059,6 +1073,33 @@ namespace Microsoft.AspNetCore.Http.Generated public new bool IsOptional { get; } } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata: IParameterBindingMetadata + { + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } + + public bool HasTryParse { get; } + + public bool HasBindAsync { get; } + + public ParameterInfo ParameterInfo { get; } + + public bool IsOptional { get; } + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs index 6be8fd336245..68f0af004651 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs @@ -33,7 +33,7 @@ public async Task MapAction_ReturnsString_Has_Metadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); Assert.Equal("text/plain", metadata.ContentTypes.Single()); - Assert.Null(metadata.Type); + Assert.Equal(typeof(string), metadata.Type); await VerifyAgainstBaselineUsingFile(compilation); } @@ -79,19 +79,21 @@ public async Task MapAction_ReturnsTaskOfString_Has_Metadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); Assert.Equal("text/plain", metadata.ContentTypes.Single()); - Assert.Null(metadata.Type); + Assert.Equal(typeof(string), metadata.Type); } [Fact] - public async Task MapAction_ReturnsTask_Has_No_Metadata() + public async Task MapAction_ReturnsTask_ProducesInferredMetadata() { var (_, compilation) = await RunGeneratorAsync(""" app.MapGet("/", Task () => Task.CompletedTask); """); var endpoint = GetEndpointFromCompilation(compilation); - var metadata = endpoint.Metadata.OfType(); - Assert.Empty(metadata); + var metadata = endpoint.Metadata.OfType().Single(); + Assert.Equal(200, metadata.StatusCode); + Assert.Equal("text/plain", metadata.ContentTypes.Single()); + Assert.Equal(typeof(void), metadata.Type); } [Fact] @@ -105,19 +107,21 @@ public async Task MapAction_ReturnsValueTaskOfString_Has_Metadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); Assert.Equal("text/plain", metadata.ContentTypes.Single()); - Assert.Null(metadata.Type); + Assert.Equal(typeof(string), metadata.Type); } [Fact] - public async Task MapAction_ReturnsValueTask_Has_No_Metadata() + public async Task MapAction_ReturnsValueTask_ProducesInferredMetadata() { var (_, compilation) = await RunGeneratorAsync(""" app.MapGet("/", ValueTask () => ValueTask.CompletedTask); """); var endpoint = GetEndpointFromCompilation(compilation); - var metadata = endpoint.Metadata.OfType(); - Assert.Empty(metadata); + var metadata = endpoint.Metadata.OfType().Single(); + Assert.Equal(200, metadata.StatusCode); + Assert.Equal("text/plain", metadata.ContentTypes.Single()); + Assert.Equal(typeof(void), metadata.Type); } [Fact] @@ -189,7 +193,7 @@ public async Task Create_AddPlaintextResponseType_AsMetadata() var responseMetadata = endpoint.Metadata.OfType().Single(); Assert.Equal("text/plain", Assert.Single(responseMetadata.ContentTypes)); - Assert.Null(responseMetadata.Type); + Assert.Equal(typeof(string), responseMetadata.Type); } [Fact] @@ -526,6 +530,8 @@ m is not Attribute2 && Assert.Collection(filteredMetadata, // Inferred AcceptsMetadata from RDF for complex type m => Assert.True(m is IAcceptsMetadata am && am.RequestType == typeof(AddsCustomParameterMetadata)), + // Parameter binding metadata inferred by RDF + m => Assert.True(m is IParameterBindingMetadata { Name: "param1" }), // Inferred ProducesResopnseTypeMetadata from RDF for complex type m => Assert.Equal(typeof(CountsDefaultEndpointMetadataPoco), ((IProducesResponseTypeMetadata)m).Type), // Metadata provided by parameters implementing IEndpointParameterMetadataProvider diff --git a/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs index 451c408d004b..a5156c7fff05 100644 --- a/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs @@ -487,6 +487,7 @@ public void Map_AddsMetadata_InCorrectOrder() Assert.Collection(metadata, m => Assert.IsAssignableFrom(m), + m => Assert.IsAssignableFrom(m), m => Assert.IsAssignableFrom(m), m => { diff --git a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml new file mode 100644 index 000000000000..97a42afc46b0 --- /dev/null +++ b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml @@ -0,0 +1,11 @@ + + + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.#ctor(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + \ No newline at end of file diff --git a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj index 376dd41c133b..16a5411f1caf 100644 --- a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj +++ b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj @@ -9,6 +9,12 @@ Microsoft.AspNetCore.Mvc.IActionResult true aspnetcore;aspnetcoremvc false + + true + false + false diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index 1f6e8042341a..e6cd088cf93a 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -27,8 +27,19 @@ public abstract class ModelMetadata : IEquatable, IModelMetadata /// public static readonly int DefaultOrder = 10000; - private static readonly ParameterBindingMethodCache ParameterBindingMethodCache - = new(throwOnInvalidMethod: false); + internal const string RequiresUnreferencedCodeMessage = "Resolving this property is not compatible with trimming, as it requires dynamic access to code that is not referenced statically."; + internal const string RequiresDynamicCodeMessage = "Resolving this property may require dynamic code generation."; + + /// + /// Exposes a feature switch to disable generating model metadata with reflection-heavy strategies. + /// This is primarily intended for use in Minimal API-based scenarios where information is derived from + /// IParameterBindingMetadata + /// + [FeatureSwitchDefinition("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported")] + [FeatureGuard(typeof(RequiresDynamicCodeAttribute))] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] + private static bool IsEnhancedModelMetadataSupported { get; } = + AppContext.TryGetSwitch("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", out var isEnhancedModelMetadataSupported) ? isEnhancedModelMetadataSupported : true; private int? _hashCode; private IReadOnlyList? _boundProperties; @@ -46,6 +57,10 @@ protected ModelMetadata(ModelMetadataIdentity identity) Identity = identity; InitializeTypeInformation(); + if (IsEnhancedModelMetadataSupported) + { + InitializeDynamicTypeInformation(); + } } /// @@ -428,11 +443,29 @@ internal IReadOnlyDictionary BoundConstructorPrope /// public abstract IReadOnlyList ValidatorMetadata { get; } + private Type? _elementType; + /// /// Gets the for elements of if that /// implements . /// - public Type? ElementType { get; private set; } + public Type? ElementType + { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + get + { + if (!IsEnhancedModelMetadataSupported) + { + throw new NotSupportedException("ElementType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); + } + return _elementType; + } + private set + { + _elementType = value; + } + } /// /// Gets a value indicating whether is a complex type. @@ -442,20 +475,46 @@ internal IReadOnlyDictionary BoundConstructorPrope /// from and without a TryParse method. Most POCO and types are therefore complex. /// Most, if not all, BCL value types are simple types. /// - public bool IsComplexType => !IsConvertibleType && !IsParseableType; + public bool IsComplexType + { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + get + { + return !IsConvertibleType && !IsParseableType; + } + } /// /// Gets a value indicating whether or not is a . /// public bool IsNullableValueType { get; private set; } + private bool? _isCollectionType; + /// /// Gets a value indicating whether or not is a collection type. /// /// /// A collection type is defined as a which is assignable to . /// - public bool IsCollectionType { get; private set; } + public bool IsCollectionType + { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + get + { + if (_isCollectionType == null) + { + throw new NotSupportedException("IsCollectionType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); + } + return _isCollectionType.Value; + } + private set + { + _isCollectionType = value; + } + } /// /// Gets a value indicating whether or not is an enumerable type. @@ -480,16 +539,52 @@ internal IReadOnlyDictionary BoundConstructorPrope /// public Type UnderlyingOrModelType { get; private set; } = default!; + private bool? _isParseableType; + /// /// Gets a value indicating whether or not has a TryParse method. /// - internal virtual bool IsParseableType { get; private set; } + internal virtual bool IsParseableType + { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + get + { + if (!_isParseableType.HasValue) + { + throw new NotSupportedException("IsParseableType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); + } + return _isParseableType.Value; + } + private set + { + _isParseableType = value; + } + } + + private bool? _isConvertibleType; /// /// Gets a value indicating whether or not has a /// from . /// - internal bool IsConvertibleType { get; private set; } + internal bool IsConvertibleType + { + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + get + { + if (!_isConvertibleType.HasValue) + { + throw new NotSupportedException("IsConvertibleType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); + + } + return _isConvertibleType.Value; + } + private set + { + _isConvertibleType = value; + } + } /// /// Gets a value indicating the NullabilityState of the value or reference type. @@ -543,6 +638,8 @@ internal void ThrowIfRecordTypeHasValidationOnProperties() } } + [RequiresUnreferencedCode("Finding the TryParse method via reflection is not trim compatible.")] + [RequiresDynamicCode("Finding the TryParse method via reflection is not native AOT compatible.")] internal static Func? FindTryParseMethod(Type modelType) { if (modelType.IsByRef) @@ -553,7 +650,7 @@ internal void ThrowIfRecordTypeHasValidationOnProperties() } modelType = Nullable.GetUnderlyingType(modelType) ?? modelType; - return ParameterBindingMethodCache.FindTryParseMethod(modelType); + return ParameterBindingMethodCache.NonThrowingInstance.FindTryParseMethod(modelType); } [MemberNotNull(nameof(_parameterMapping), nameof(_boundConstructorPropertyMapping))] @@ -651,16 +748,11 @@ private void InitializeTypeInformation() { Debug.Assert(ModelType != null); - IsConvertibleType = TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); - IsParseableType = FindTryParseMethod(ModelType) is not null; IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType; UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; HasDefaultValue = MetadataKind == ModelMetadataKind.Parameter && Identity.ParameterInfo!.HasDefaultValue; - var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>)); - IsCollectionType = collectionType != null; - var nullabilityContext = new NullabilityInfoContext(); var nullability = MetadataKind switch { @@ -670,27 +762,30 @@ private void InitializeTypeInformation() }; NullabilityState = nullability?.ReadState ?? NullabilityState.Unknown; - if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) - { - // Do nothing, not Enumerable. - } - else if (ModelType.IsArray) + if (ModelType.IsArray) { IsEnumerableType = true; ElementType = ModelType.GetElementType()!; } - else + } + + [RequiresUnreferencedCode("Using ModelMetadata with IsEnhancedModelMetadataSupport=true is not trim compatible.")] + [RequiresDynamicCode("Using ModelMetadata with IsEnhancedModelMetadataSupport=true is not native AOT compatible.")] + private void InitializeDynamicTypeInformation() + { + Debug.Assert(ModelType != null); + IsConvertibleType = TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); + IsParseableType = FindTryParseMethod(ModelType) is not null; + + var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>)); + _isCollectionType = collectionType != null; + + if (ModelType != typeof(string) && !ModelType.IsArray && typeof(IEnumerable).IsAssignableFrom(ModelType)) { IsEnumerableType = true; - var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable<>)); - ElementType = enumerableType?.GenericTypeArguments[0]!; - - if (ElementType == null) - { - // ModelType implements IEnumerable but not IEnumerable. - ElementType = typeof(object); - } + // Apply fallback when ModelType implements IEnumerable but not IEnumerable. + ElementType = enumerableType?.GenericTypeArguments[0] ?? typeof(object); Debug.Assert( ElementType != null, diff --git a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs index cc371e39a4ec..6844fb8cce7a 100644 --- a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs +++ b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs @@ -6,6 +6,7 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.DotNet.RemoteExecutor; namespace Microsoft.AspNetCore.Mvc.ModelBinding; @@ -406,6 +407,40 @@ public void GetMetadataForProperties_ByDefaultThrows_NotImplementedException() var result = Assert.Throws(() => metadata.GetMetadataForProperties(typeof(string))); } + [Fact] + public void DynamicPropertiesThrowWhenIsDynamicCodeSupportedIsTrue() + { + var options = new RemoteInvokeOptions(); + + options.RuntimeConfigurationOptions.Add("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", false); + using var remoteHandle = RemoteExecutor.Invoke(static () => + { + var metadata = new TestModelMetadata(typeof(DateTime)); + Assert.Throws(() => metadata.ElementType); + Assert.Throws(() => metadata.IsParseableType); + Assert.Throws(() => metadata.IsConvertibleType); + Assert.Throws(() => metadata.IsComplexType); + Assert.Throws(() => metadata.IsCollectionType); + }, options); + } + + [Fact] + public void DynamicPropertiesSetWhenIsDynamicCodeSupportedIsTrue() + { + var options = new RemoteInvokeOptions(); + + options.RuntimeConfigurationOptions.Add("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", true); + using var remoteHandle = RemoteExecutor.Invoke(static () => + { + var metadata = new TestModelMetadata(typeof(DateTime)); + Assert.Null(metadata.ElementType); + Assert.True(metadata.IsParseableType); + Assert.False(metadata.IsCollectionType); + Assert.True(metadata.IsConvertibleType); + Assert.False(metadata.IsComplexType); + }, options); + } + private class TestModelMetadata : ModelMetadata { private string _displayName; diff --git a/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs index d1f151812ae6..37d7651a1081 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs @@ -95,6 +95,7 @@ private ICollection GetApiResponseTypes( type, defaultErrorType, contentTypes, + out var _, responseTypeMetadataProviders); foreach (var responseType in responseTypesFromProvider) @@ -134,11 +135,13 @@ private ICollection GetApiResponseTypes( internal static Dictionary ReadResponseMetadata( IReadOnlyList responseMetadataAttributes, Type? type, - Type defaultErrorType, + Type? defaultErrorType, MediaTypeCollection contentTypes, + out bool errorSetByDefault, IEnumerable? responseTypeMetadataProviders = null, IModelMetadataProvider? modelMetadataProvider = null) { + errorSetByDefault = false; var results = new Dictionary(); // Get the content type that the action explicitly set to support. @@ -184,8 +187,8 @@ internal static Dictionary ReadResponseMetadata( { // Determine whether or not the type was provided by the user. If so, favor it over the default // error type for 4xx client errors if no response type is specified.. - var setByDefault = metadataAttribute is ProducesResponseTypeAttribute { IsResponseTypeSetByDefault: true }; - apiResponseType.Type = setByDefault ? defaultErrorType : apiResponseType.Type; + errorSetByDefault = metadataAttribute is ProducesResponseTypeAttribute { IsResponseTypeSetByDefault: true }; + apiResponseType.Type = errorSetByDefault ? defaultErrorType : apiResponseType.Type; } else if (apiResponseType.IsDefaultResponse) { @@ -233,7 +236,7 @@ internal static Dictionary ReadResponseMetadata( StatusCode = statusCode, }; - if (apiResponseType.Type == typeof(void)) + if (apiResponseType.Type == null) { if (type != null && (statusCode == StatusCodes.Status200OK || statusCode == StatusCodes.Status201Created)) { diff --git a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs index 4aef98aabd96..0e499dacddd1 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.Abstractions; @@ -20,6 +21,8 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer; /// Implements a provider of for actions represented /// by . /// +[RequiresUnreferencedCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] +[RequiresDynamicCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] public class DefaultApiDescriptionProvider : IApiDescriptionProvider { private readonly MvcOptions _mvcOptions; @@ -268,7 +271,7 @@ parameter.ModelMetadata is DefaultModelMetadata defaultModelMetadata && !defaultModelMetadata.Attributes.Attributes.OfType().Any()) { // If we didn't see the parameter in the route and no FromRoute metadata is set, it probably means - // the parameter binding source was inferred (InferParameterBindingInfoConvention) + // the parameter binding source was inferred (InferParameterBindingInfoConvention) // probably because another route to this action contains it as route parameter and // will be removed from the API description // https://github.com/dotnet/aspnetcore/issues/26234 @@ -532,6 +535,8 @@ public ApiParameterDescriptionContext( } } + [RequiresUnreferencedCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] + [RequiresDynamicCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] private sealed class PseudoModelBindingVisitor { public PseudoModelBindingVisitor(ApiParameterContext context, ParameterDescriptor parameter) diff --git a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs index 8e26708c6bc8..35ef2e0c3275 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -16,6 +17,7 @@ public static class MvcApiExplorerMvcCoreBuilderExtensions /// /// The . /// The . + [RequiresUnreferencedCode("MVC does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder) { ArgumentNullException.ThrowIfNull(builder); @@ -25,6 +27,7 @@ public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder) } // Internal for testing. + [RequiresUnreferencedCode("MVC does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] internal static void AddApiExplorerServices(IServiceCollection services) { services.TryAddSingleton(); diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 8766b063f412..ee46c1e9f689 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Internal; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Mvc.ApiExplorer; @@ -25,7 +24,6 @@ internal sealed class EndpointMetadataApiDescriptionProvider : IApiDescriptionPr private readonly EndpointDataSource _endpointDataSource; private readonly IHostEnvironment _environment; private readonly IServiceProviderIsService? _serviceProviderIsService; - private readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); private readonly ParameterPolicyFactory _parameterPolicyFactory; // Executes before MVC's DefaultApiDescriptionProvider and GrpcJsonTranscodingDescriptionProvider for no particular reason. @@ -115,10 +113,11 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string }; var hasBodyOrFormFileParameter = false; + var parameters = routeEndpoint.Metadata.GetOrderedMetadata(); - foreach (var parameter in PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache)) + foreach (var parameter in parameters) { - var parameterDescription = CreateApiParameterDescription(parameter, routeEndpoint.RoutePattern, disableInferredBody); + var parameterDescription = CreateApiParameterDescription(parameter, routeEndpoint, disableInferredBody); if (parameterDescription is { }) { @@ -169,9 +168,10 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string return apiDescription; } - private ApiParameterDescription? CreateApiParameterDescription(ParameterInfo parameter, RoutePattern pattern, bool disableInferredBody) + private ApiParameterDescription? CreateApiParameterDescription(IParameterBindingMetadata parameter, RouteEndpoint routeEndpoint, bool disableInferredBody) { - var (source, name, allowEmpty, paramType) = GetBindingSourceAndName(parameter, pattern, disableInferredBody); + var pattern = routeEndpoint.RoutePattern; + var (source, name, _, paramType) = GetBindingSourceAndName(parameter, routeEndpoint, disableInferredBody); // Services are ignored because they are not request parameters. if (source == BindingSource.Services) @@ -179,22 +179,19 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string return null; } - // Determine the "requiredness" based on nullability, default value or if allowEmpty is set - var nullabilityContext = new NullabilityInfoContext(); - var nullability = nullabilityContext.Create(parameter); - var isOptional = parameter is PropertyAsParameterInfo argument - ? argument.IsOptional || allowEmpty - : parameter.HasDefaultValue || nullability.ReadState != NullabilityState.NotNull || allowEmpty; - var parameterDescriptor = CreateParameterDescriptor(parameter, pattern); - var routeInfo = CreateParameterRouteInfo(pattern, parameter, isOptional); + // Use the optionality status determined by the code generation layer which accounts for + // nullability, default values, and the whether or not `[FromBody(AllowEmpty = true)]`. + var isOptional = parameter.IsOptional; + var parameterDescriptor = CreateParameterDescriptor(parameter.ParameterInfo, pattern); + var routeInfo = CreateParameterRouteInfo(pattern, parameter.ParameterInfo, isOptional); return new ApiParameterDescription { Name = name, ModelMetadata = CreateModelMetadata(paramType), Source = source, - DefaultValue = parameter.DefaultValue, - Type = parameter.ParameterType, + DefaultValue = parameter.ParameterInfo.DefaultValue, + Type = parameter.ParameterInfo.ParameterType, IsRequired = !isOptional, ParameterDescriptor = parameterDescriptor, RouteInfo = routeInfo @@ -250,47 +247,48 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param // TODO: Share more of this logic with RequestDelegateFactory.CreateArgument(...) using RequestDelegateFactoryUtilities // which is shared source. - private (BindingSource, string, bool, Type) GetBindingSourceAndName(ParameterInfo parameter, RoutePattern pattern, bool disableInferredBody) + private (BindingSource, string, bool, Type) GetBindingSourceAndName(IParameterBindingMetadata parameter, RouteEndpoint routeEndpoint, bool disableInferredBody) { - var attributes = parameter.GetCustomAttributes(); - + var pattern = routeEndpoint.RoutePattern; + var attributes = parameter.ParameterInfo.GetCustomAttributes(); + var parameterType = parameter.ParameterInfo.ParameterType; if (attributes.OfType().FirstOrDefault() is { } routeAttribute) { var parameterName = parameter.Name ?? string.Empty; var name = pattern.GetParameter(parameterName)?.Name ?? parameterName; - return (BindingSource.Path, routeAttribute.Name ?? name, false, parameter.ParameterType); + return (BindingSource.Path, routeAttribute.Name ?? name, false, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } queryAttribute) { - return (BindingSource.Query, queryAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Query, queryAttribute.Name ?? parameter.Name ?? string.Empty, false, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } headerAttribute) { - return (BindingSource.Header, headerAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Header, headerAttribute.Name ?? parameter.Name ?? string.Empty, false, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } fromBodyAttribute) { - return (BindingSource.Body, parameter.Name ?? string.Empty, fromBodyAttribute.AllowEmpty, parameter.ParameterType); + return (BindingSource.Body, parameter.Name ?? string.Empty, fromBodyAttribute.AllowEmpty, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } fromFormAttribute) { - return (BindingSource.FormFile, fromFormAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.FormFile, fromFormAttribute.Name ?? parameter.Name ?? string.Empty, false, parameterType); } - else if (parameter.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType) || typeof(FromKeyedServicesAttribute) == a.AttributeType) || - parameter.ParameterType == typeof(HttpContext) || - parameter.ParameterType == typeof(HttpRequest) || - parameter.ParameterType == typeof(HttpResponse) || - parameter.ParameterType == typeof(ClaimsPrincipal) || - parameter.ParameterType == typeof(CancellationToken) || - ParameterBindingMethodCache.HasBindAsyncMethod(parameter) || - _serviceProviderIsService?.IsService(parameter.ParameterType) == true) - { - return (BindingSource.Services, parameter.Name ?? string.Empty, false, parameter.ParameterType); + else if (parameter.ParameterInfo.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType) || typeof(FromKeyedServicesAttribute) == a.AttributeType) || + parameterType == typeof(HttpContext) || + parameterType == typeof(HttpRequest) || + parameterType == typeof(HttpResponse) || + parameterType == typeof(ClaimsPrincipal) || + parameterType == typeof(CancellationToken) || + parameter.HasBindAsync || + _serviceProviderIsService?.IsService(parameterType) == true) + { + return (BindingSource.Services, parameter.Name ?? string.Empty, false, parameterType); } - else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) + else if (parameterType == typeof(string) || (!parameterType.IsArray && parameterType != typeof(StringValues) && parameter.HasTryParse)) { // complex types will display as strings since they use custom parsing via TryParse on a string - var displayType = EndpointModelMetadata.GetDisplayType(parameter.ParameterType); + var displayType = EndpointModelMetadata.GetDisplayType(parameterType); // Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here. if (parameter.Name is { } name && pattern.GetParameter(name) is { } routeParam) @@ -302,20 +300,20 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param return (BindingSource.Query, parameter.Name ?? string.Empty, false, displayType); } } - else if (parameter.ParameterType == typeof(IFormFile) || parameter.ParameterType == typeof(IFormFileCollection)) + else if (parameterType == typeof(IFormFile) || parameterType == typeof(IFormFileCollection)) { - return (BindingSource.FormFile, parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.FormFile, parameter.Name ?? string.Empty, false, parameterType); } else if (disableInferredBody && ( - parameter.ParameterType == typeof(string[]) || - parameter.ParameterType == typeof(StringValues) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)) )) + parameterType == typeof(string[]) || + parameterType == typeof(StringValues) || + (parameterType.IsArray && parameter.HasTryParse))) { - return (BindingSource.Query, parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Query, parameter.Name ?? string.Empty, false, parameterType); } else { - return (BindingSource.Body, parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Body, parameter.Name ?? string.Empty, false, parameterType); } } @@ -326,11 +324,6 @@ private static void AddSupportedResponseTypes( { var responseType = returnType; - if (CoercedAwaitableInfo.IsTypeAwaitable(responseType, out var coercedAwaitableInfo)) - { - responseType = coercedAwaitableInfo.AwaitableInfo.ResultType; - } - // Can't determine anything about IResults yet that's not from extra metadata. IResult could help here. if (typeof(IResult).IsAssignableFrom(responseType)) { @@ -346,7 +339,7 @@ private static void AddSupportedResponseTypes( var contentTypes = new MediaTypeCollection(); var responseProviderMetadataTypes = ApiResponseTypeProvider.ReadResponseMetadata( - responseProviderMetadata, responseType, defaultErrorType, contentTypes); + responseProviderMetadata, responseType, defaultErrorType, contentTypes, out var errorSetByDefault); var producesResponseMetadataTypes = ApiResponseTypeProvider.ReadResponseMetadata(producesResponseMetadata, responseType); // We favor types added via the extension methods (which implements IProducesResponseTypeMetadata) @@ -357,9 +350,12 @@ private static void AddSupportedResponseTypes( { foreach (var apiResponseType in responseMetadataTypes) { - // void means no response type was specified by the metadata, so use whatever we inferred. - // ApiResponseTypeProvider should never return ApiResponseTypes with null Type, but it doesn't hurt to check. - if (apiResponseType.Type is null || apiResponseType.Type == typeof(void)) + // In some context, a typeof(void) return means that no response type was specified by the metadata. This can happen + // if a user applied a [ProducesResponseType] attribute without a default type parameter. In this case, we should use the + // response type inferred from the return type of the handler. For minimal API scenarios, where `typeof(void)` can be inferred + // by the framework for handlers that return awaitables, we will only treat `typeof(void)` as a null type that should fall back to the + // inference logic if it has been set as the default error type to retain back-compat. + if (apiResponseType.Type is null || (apiResponseType.Type == typeof(void) && errorSetByDefault)) { apiResponseType.Type = responseType; } diff --git a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj index 075d7b90a560..c5991d82ea44 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj +++ b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj @@ -7,6 +7,7 @@ true aspnetcore;aspnetcoremvc false + true diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 526f9e150bde..c16601586147 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -71,12 +71,12 @@ static void AssertCustomRequestFormat(ApiDescription apiDescription) AssertCustomRequestFormat(GetApiDescription( [Consumes("application/custom")] (InferredJsonClass fromBody) => - { })); + { }, httpMethods: ["POST"])); AssertCustomRequestFormat(GetApiDescription( [Consumes("application/custom")] ([FromBody] int fromBody) => - { })); + { }, httpMethods: ["POST"])); } [Fact] @@ -85,7 +85,7 @@ public void AddsMultipleRequestFormatsFromMetadata() var apiDescription = GetApiDescription( [Consumes("application/custom0", "application/custom1")] (InferredJsonClass fromBody) => - { }); + { }, httpMethods: ["POST"]); Assert.Equal(2, apiDescription.SupportedRequestFormats.Count); @@ -119,7 +119,7 @@ public void AddsMultipleRequestFormatsFromMetadataWithRequiredBodyParameter() var apiDescription = GetApiDescription( [Consumes(typeof(InferredJsonClass), "application/custom0", "application/custom1", IsOptional = false)] (InferredJsonClass fromBody) => - { }); + { }, httpMethods: ["POST"]); Assert.Equal(2, apiDescription.SupportedRequestFormats.Count); @@ -188,14 +188,23 @@ public void AddsResponseFormatFromMetadata() [Produces("application/custom")] () => new InferredJsonClass()); - var responseType = Assert.Single(apiDescription.SupportedResponseTypes); + Assert.Equal(2, apiDescription.SupportedResponseTypes.Count); - Assert.Equal(201, responseType.StatusCode); - Assert.Equal(typeof(TimeSpan), responseType.Type); - Assert.Equal(typeof(TimeSpan), responseType.ModelMetadata?.ModelType); + var inferredResponseType = apiDescription.SupportedResponseTypes[0]; - var responseFormat = Assert.Single(responseType.ApiResponseFormats); - Assert.Equal("application/custom", responseFormat.MediaType); + Assert.Equal(200, inferredResponseType.StatusCode); + Assert.Equal(typeof(InferredJsonClass), inferredResponseType.Type); + Assert.Equal(typeof(InferredJsonClass), inferredResponseType.ModelMetadata?.ModelType); + + Assert.Equal(["application/json", "application/custom"], inferredResponseType.ApiResponseFormats.Select(f => f.MediaType)); + + var annotatedResponseType = apiDescription.SupportedResponseTypes[1]; + + Assert.Equal(201, annotatedResponseType.StatusCode); + Assert.Equal(typeof(TimeSpan), annotatedResponseType.Type); + Assert.Equal(typeof(TimeSpan), annotatedResponseType.ModelMetadata?.ModelType); + + Assert.Equal("application/custom", Assert.Single(annotatedResponseType.ApiResponseFormats).MediaType); } [Fact] @@ -206,9 +215,18 @@ public void AddsMultipleResponseFormatsFromMetadataWithPoco() [ProducesResponseType(StatusCodes.Status400BadRequest)] () => new InferredJsonClass()); - Assert.Equal(2, apiDescription.SupportedResponseTypes.Count); + Assert.Equal(3, apiDescription.SupportedResponseTypes.Count); - var createdResponseType = apiDescription.SupportedResponseTypes[0]; + var rdfInferredResponseType = apiDescription.SupportedResponseTypes[0]; + + Assert.Equal(200, rdfInferredResponseType.StatusCode); + Assert.Equal(typeof(InferredJsonClass), rdfInferredResponseType.Type); + Assert.Equal(typeof(InferredJsonClass), rdfInferredResponseType.ModelMetadata?.ModelType); + + var rdfInferredResponseFormat = Assert.Single(rdfInferredResponseType.ApiResponseFormats); + Assert.Equal("application/json", rdfInferredResponseFormat.MediaType); + + var createdResponseType = apiDescription.SupportedResponseTypes[1]; Assert.Equal(201, createdResponseType.StatusCode); Assert.Equal(typeof(TimeSpan), createdResponseType.Type); @@ -217,7 +235,7 @@ public void AddsMultipleResponseFormatsFromMetadataWithPoco() var createdResponseFormat = Assert.Single(createdResponseType.ApiResponseFormats); Assert.Equal("application/json", createdResponseFormat.MediaType); - var badRequestResponseType = apiDescription.SupportedResponseTypes[1]; + var badRequestResponseType = apiDescription.SupportedResponseTypes[2]; Assert.Equal(400, badRequestResponseType.StatusCode); Assert.Equal(typeof(InferredJsonClass), badRequestResponseType.Type); @@ -421,8 +439,8 @@ public void AddsFromHeaderParameterAsHeader() public void DoesNotAddFromServiceParameterAsService() { Assert.Empty(GetApiDescription((IInferredServiceInterface foo) => { }).ParameterDescriptions); - Assert.Empty(GetApiDescription(([FromServices] int foo) => { }).ParameterDescriptions); - Assert.Empty(GetApiDescription(([FromKeyedServices("foo")] int foo) => { }).ParameterDescriptions); + Assert.Empty(GetApiDescription(([FromServices] InferredServiceClass foo) => { }).ParameterDescriptions); + Assert.Empty(GetApiDescription(([FromKeyedServices("foo")] InferredServiceClass foo) => { }).ParameterDescriptions); Assert.Empty(GetApiDescription((HttpContext context) => { }).ParameterDescriptions); Assert.Empty(GetApiDescription((HttpRequest request) => { }).ParameterDescriptions); Assert.Empty(GetApiDescription((HttpResponse response) => { }).ParameterDescriptions); @@ -443,8 +461,8 @@ static void AssertBodyParameter(ApiDescription apiDescription, string expectedNa Assert.Equal(BindingSource.Body, param.Source); } - AssertBodyParameter(GetApiDescription((InferredJsonClass foo) => { }), "foo", typeof(InferredJsonClass)); - AssertBodyParameter(GetApiDescription(([FromBody] int bar) => { }), "bar", typeof(int)); + AssertBodyParameter(GetApiDescription((InferredJsonClass foo) => { }, httpMethods: ["POST"]), "foo", typeof(InferredJsonClass)); + AssertBodyParameter(GetApiDescription(([FromBody] int bar) => { }, httpMethods: ["POST"]), "bar", typeof(int)); } [Fact] @@ -459,7 +477,7 @@ public void AddsDefaultValueFromParameters() [Fact] public void AddsMultipleParameters() { - var apiDescription = GetApiDescription(([FromRoute] int foo, int bar, InferredJsonClass fromBody) => { }); + var apiDescription = GetApiDescription(([FromRoute] int foo, int bar, InferredJsonClass fromBody) => { }, httpMethods: ["POST"]); Assert.Equal(3, apiDescription.ParameterDescriptions.Count); var fooParam = apiDescription.ParameterDescriptions[0]; @@ -517,14 +535,14 @@ static void AssertParameters(ApiDescription apiDescription, string capturedName ); } - AssertParameters(GetApiDescription(([AsParameters] ArgumentListClass req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListClassWithReadOnlyProperties req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListStruct req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecord req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordStruct req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutPositionalParameters req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}"), "foo"); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{Foo}")); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListClass req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListClassWithReadOnlyProperties req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListStruct req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecord req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordStruct req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutPositionalParameters req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}", httpMethods: ["POST"]), "foo"); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{Foo}", httpMethods: ["POST"])); } #nullable enable @@ -679,8 +697,8 @@ public void RespectsProducesProblemExtensionMethod() // Assert var apiDescription = Assert.Single(context.Results); - var responseTypes = Assert.Single(apiDescription.SupportedResponseTypes); - Assert.Equal(typeof(ProblemDetails), responseTypes.Type); + Assert.Contains(apiDescription.SupportedResponseTypes, m => m is { StatusCode: 400, Type: { } type } && type == typeof(ProblemDetails)); + Assert.Contains(apiDescription.SupportedResponseTypes, m => m is { StatusCode: 200, Type: { } type } && type == typeof(string)); } [Fact] @@ -1362,9 +1380,26 @@ private static IList GetApiDescriptions( var attributes = methodInfo.GetCustomAttributes(); var context = new ApiDescriptionProviderContext(Array.Empty()); - var httpMethodMetadata = new HttpMethodMetadata(httpMethods ?? new[] { "GET" }); - var metadataItems = new List(attributes) { methodInfo, httpMethodMetadata }; - var endpointMetadata = new EndpointMetadataCollection(metadataItems.ToArray()); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(new InferredServiceClass()); + serviceCollection.AddKeyedSingleton("foo", new InferredServiceClass()); + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var shouldDisableInferredBodyForMethod = httpMethods is null || httpMethods.Any(method => + // GET, DELETE, HEAD, CONNECT, TRACE, and OPTIONS normally do not contain bodies + method.Equals(HttpMethods.Get, StringComparison.Ordinal) || + method.Equals(HttpMethods.Delete, StringComparison.Ordinal) || + method.Equals(HttpMethods.Head, StringComparison.Ordinal) || + method.Equals(HttpMethods.Options, StringComparison.Ordinal) || + method.Equals(HttpMethods.Trace, StringComparison.Ordinal) || + method.Equals(HttpMethods.Connect, StringComparison.Ordinal)); + + var options = new RequestDelegateFactoryOptions { ServiceProvider = serviceProvider, DisableInferBodyFromParameters = shouldDisableInferredBodyForMethod }; + var requestDelegateResult = RequestDelegateFactory.Create(methodInfo, options: options); + + var httpMethodMetadata = new HttpMethodMetadata(httpMethods ?? ["GET"]); + var metadataItems = new List(requestDelegateResult.EndpointMetadata) { methodInfo, httpMethodMetadata }; + var endpointMetadata = new EndpointMetadataCollection([.. metadataItems, .. attributes]); var routePattern = RoutePatternFactory.Parse(pattern ?? "/"); var endpoint = new RouteEndpoint(httpContext => Task.CompletedTask, routePattern, 0, endpointMetadata, displayName); @@ -1387,7 +1422,7 @@ private static IList GetApiDescriptions( private static TestEndpointRouteBuilder CreateBuilder() => new TestEndpointRouteBuilder(new ApplicationBuilder(TestServiceProvider.Instance)); - private static ApiDescription GetApiDescription(Delegate action, string? pattern = null, string? displayName = null, IEnumerable? httpMethods = null) => + private static ApiDescription GetApiDescription(Delegate action, string? pattern = null, string? displayName = null, IEnumerable? httpMethods = null, RequestDelegateFactoryOptions? options = null) => Assert.Single(GetApiDescriptions(action, pattern, displayName: displayName, httpMethods: httpMethods)); private static void TestAction() @@ -1410,6 +1445,10 @@ private interface IInferredJsonInterface { } + private class InferredServiceClass : IInferredServiceInterface + { + } + private class ServiceProviderIsService : IServiceProviderIsService { public bool IsService(Type serviceType) => serviceType == typeof(IInferredServiceInterface); diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml new file mode 100644 index 000000000000..450566e81932 --- /dev/null +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml @@ -0,0 +1,773 @@ + + + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.MapAreaControllerRoute(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder,System.String,System.String,System.String,System.Object,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.MapControllerRoute(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder,System.String,System.String,System.Object,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.MvcApplicationBuilderExtensions.<>c.<UseMvcWithDefaultRoute>b__1_0(Microsoft.AspNetCore.Routing.IRouteBuilder) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.MvcAreaRouteBuilderExtensions.MapAreaRoute(Microsoft.AspNetCore.Routing.IRouteBuilder,System.String,System.String,System.String,System.Object,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.AcceptedAtActionResult.#ctor(System.String,System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.AcceptedAtRouteResult.#ctor(System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.InferParameterBindingInfoConvention.IsComplexTypeParameter(Microsoft.AspNetCore.Mvc.ApplicationModels.ParameterModel,Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata@) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.AssemblyPart.get_Types + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.RelatedAssemblyAttribute.AssemblyLoadContextWrapper.LoadFromAssemblyPath(System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.CreatedAtActionResult.#ctor(System.String,System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.CreatedAtRouteResult.#ctor(System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.<ReadRequestBodyAsync>d__8.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.<WriteResponseBodyAsync>d__5.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerCache.GetCachedResult(Microsoft.AspNetCore.Mvc.ControllerContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor.<ExecuteAsync>d__4.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.JsonOptions.CreateDefaultTypeResolver + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.<BindComplexCollectionFromIndexes>d__22.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.<BindPropertyAsync>d__15.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.<BindProperty>d__11.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.HeaderModelBinderProvider.IsSimpleType(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder.#ctor(System.Type,Microsoft.Extensions.Logging.ILoggerFactory) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.#cctor + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.CreateTryParseOperation(System.Type) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetRecordTypeConstructor(System.Type,System.Reflection.ConstructorInfo[]) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.CalculateHasValidators(System.Collections.Generic.HashSet{Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata},Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.get_ElementMetadata + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.get_ValidateChildren + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreatePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity,Microsoft.Extensions.Internal.PropertyHelper) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ClearValidationStateForModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.PropertyValueSetter.SetValue(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.DefaultCollectionValidationStrategy.GetEnumeratorForElementType(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata@,System.String@,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterActionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Routing.RouteData) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterActionMethodImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object},System.Object,Microsoft.AspNetCore.Mvc.IActionResult) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterActionResultImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,Microsoft.AspNetCore.Mvc.IActionResult) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnActionExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnActionExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnActionExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnAuthorizationAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnAuthorizationImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnExceptionAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnExceptionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResourceExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResourceExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResourceExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResultExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResultExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResultExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeActionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Routing.RouteData) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeActionMethodImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object},System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeActionResultImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,Microsoft.AspNetCore.Mvc.IActionResult) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnActionExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnActionExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnActionExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnAuthorizationAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnAuthorizationImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnExceptionAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnExceptionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResourceExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResourceExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResourceExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResultExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResultExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResultExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.RedirectToActionResult.#ctor(System.String,System.String,System.Object,System.Boolean,System.Boolean,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.RedirectToPageResult.#ctor(System.String,System.String,System.Object,System.Boolean,System.Boolean,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.RedirectToRouteResult.#ctor(System.String,System.Object,System.Boolean,System.Boolean,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory.AddActionDataToBuilder(Microsoft.AspNetCore.Builder.EndpointBuilder,System.Collections.Generic.HashSet{System.String},Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,System.String,Microsoft.AspNetCore.Routing.RouteValueDictionary,System.Boolean,System.Boolean,System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}}) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory.AddEndpoints(System.Collections.Generic.List{Microsoft.AspNetCore.Http.Endpoint},System.Collections.Generic.HashSet{System.String},Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,System.Collections.Generic.IReadOnlyList{Microsoft.AspNetCore.Mvc.Routing.ConventionalRouteEntry},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Boolean,Microsoft.AspNetCore.Routing.Patterns.RoutePattern) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.ConventionalRouteEntry.#ctor(System.String,System.String,Microsoft.AspNetCore.Routing.RouteValueDictionary,System.Collections.Generic.IDictionary{System.String,System.Object},Microsoft.AspNetCore.Routing.RouteValueDictionary,System.Int32,System.Collections.Generic.List{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.List{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}}) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper.RouteUrl(Microsoft.AspNetCore.Mvc.Routing.UrlRouteContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.UrlHelperBase.GetValuesDictionary(System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Page(Microsoft.AspNetCore.Mvc.IUrlHelper,System.String,System.String,System.Object,System.String,System.String,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Routing.ControllerLinkGeneratorExtensions.CreateAddress(Microsoft.AspNetCore.Http.HttpContext,System.String,System.String,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Routing.PageLinkGeneratorExtensions.CreateAddress(Microsoft.AspNetCore.Http.HttpContext,System.String,System.String,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.Extensions.DependencyInjection.MvcCoreRouteOptionsSetup.Configure(Microsoft.AspNetCore.Routing.RouteOptions) + + + ILLink + IL2026 + member + M:Microsoft.Extensions.Internal.PropertyActivator`1.#ctor(System.Reflection.PropertyInfo,System.Func{`0,System.Object}) + + + ILLink + IL2057 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.ProvideApplicationPartFactoryAttribute.GetFactoryType + + + ILLink + IL2060 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.TryGetReader(System.Type,System.Func{System.Object,System.Threading.CancellationToken,System.Threading.Tasks.Task{System.Collections.ICollection}}@) + + + ILLink + IL2060 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.PropertyValueSetter.SetValue(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object,System.Object) + + + ILLink + IL2060 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.DefaultCollectionValidationStrategy.<>c.<GetEnumeratorForElementType>b__5_0(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.CreateConfigureDelegate(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Formatters.InputFormatter.GetDefaultValueForType(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.TryGetReader(System.Type,System.Func{System.Object,System.Threading.CancellationToken,System.Threading.Tasks.Task{System.Collections.ICollection}}@) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.TypeActivatorCache.<>c.<#ctor>b__3_0(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BinderTypeModelBinder.#ctor(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.CreateInstance(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder.#ctor(System.Type,Microsoft.Extensions.Logging.ILoggerFactory) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetRecordTypeConstructor(System.Type,System.Reflection.ConstructorInfo[]) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertTo(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2067 + member + M:Microsoft.Extensions.Internal.PropertyActivator`1.GetActivatableProperties(System.Type,System.Type,System.Boolean) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ApiConventionMethodAttribute.GetConventionMethod(System.Type,System.String) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ApiConventionTypeAttribute.EnsureValid(System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.FindMethod(System.Type,System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.HasParameterlessConstructor(System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.<GetRecordTypeConstructor>g__IsRecordType|2_0(System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetBoundConstructor(System.Type) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartFactory.GetApplicationPartFactory(System.Reflection.Assembly) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.CreateActivator(Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.ParameterDefaultValues.GetParameterDefaultValue(System.Reflection.ParameterInfo) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.Log.AttemptingToBindCollectionUsingIndices(Microsoft.Extensions.Logging.ILogger,Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.BuildFactoryExpression(System.Reflection.ConstructorInfo,System.Linq.Expressions.Expression) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreatePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelAttributes.GetAttributesForProperty(System.Type,System.Reflection.PropertyInfo,System.Type) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.GetCompatibleCollection``1(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext,System.Nullable{System.Int32}) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.TypeFilterAttribute.CreateInstance(System.IServiceProvider) + + + ILLink + IL2072 + member + M:Microsoft.Extensions.DependencyInjection.MvcCoreMvcBuilderExtensions.AddControllersAsServices(Microsoft.Extensions.DependencyInjection.IMvcBuilder) + + + ILLink + IL2072 + member + M:Microsoft.Extensions.DependencyInjection.MvcCoreMvcCoreBuilderExtensions.AddControllersAsServices(Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ApiExplorer.ApiConventionResult.GetConventionMethod(System.Reflection.MethodInfo,Microsoft.AspNetCore.Mvc.ApiConventionTypeAttribute[]) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + \ No newline at end of file diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj index 4882542748ae..2f0ca390d01f 100644 --- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj @@ -16,6 +16,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute aspnetcore;aspnetcoremvc false enable + + true + false + false diff --git a/src/OpenApi/OpenApi.slnf b/src/OpenApi/OpenApi.slnf index 1358a85267ae..3d616bfa4daf 100644 --- a/src/OpenApi/OpenApi.slnf +++ b/src/OpenApi/OpenApi.slnf @@ -13,8 +13,10 @@ "src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj", "src\\OpenApi\\sample\\Sample.csproj", "src\\OpenApi\\src\\Microsoft.AspNetCore.OpenApi.csproj", - "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests.csproj", - "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj" + "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj", + "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests\\Microsoft.AspNetCore.OpenApi.Tests.csproj", + "src\\OpenApi\\sample\\Sample.csproj", + "src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj" ] } } diff --git a/src/OpenApi/perf/Microbenchmarks/Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj b/src/OpenApi/perf/Microbenchmarks/Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj index df6d36dd7dca..31cecda232d3 100644 --- a/src/OpenApi/perf/Microbenchmarks/Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj +++ b/src/OpenApi/perf/Microbenchmarks/Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/OpenApi/sample/Sample.csproj b/src/OpenApi/sample/Sample.csproj index aa85248ddd31..31a0291457cc 100644 --- a/src/OpenApi/sample/Sample.csproj +++ b/src/OpenApi/sample/Sample.csproj @@ -22,8 +22,8 @@ - - + + diff --git a/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj b/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj index 496c4efabb3e..65688d2eb269 100644 --- a/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj +++ b/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj @@ -7,7 +7,7 @@ true Provides APIs for annotating route handler endpoints in ASP.NET Core with OpenAPI annotations. - true + true true $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs index cc6c04d3610f..f3f9aba6c16f 100644 --- a/src/OpenApi/src/Services/OpenApiDocumentService.cs +++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs @@ -362,12 +362,17 @@ private static bool IsRequired(ApiParameterDescription parameter) // in the same endpoint. if (description.TryGetFormParameters(out var formParameters)) { - return await GetFormRequestBody(description.SupportedRequestFormats, formParameters, cancellationToken); + var endpointMetadata = description.ActionDescriptor.EndpointMetadata; + return await GetFormRequestBody(description.SupportedRequestFormats, formParameters, endpointMetadata, cancellationToken); } return null; } - private async Task GetFormRequestBody(IList supportedRequestFormats, IEnumerable formParameters, CancellationToken cancellationToken) + private async Task GetFormRequestBody( + IList supportedRequestFormats, + IEnumerable formParameters, + IList endpointMetadata, + CancellationToken cancellationToken) { if (supportedRequestFormats.Count == 0) { @@ -425,11 +430,17 @@ private async Task GetFormRequestBody(IList() + .SingleOrDefault(parameter => parameter.Name == description.Name)? + .HasTryParse == false; if (hasMultipleFormParameters) { // Here and below: POCOs do not need to be need under their parameter name in the grouping. // The form-binding implementation will capture them implicitly. - if (description.ModelMetadata.IsComplexType) + if (isComplexType) { schema.AllOf.Add(parameterSchema); } @@ -447,7 +458,7 @@ private async Task GetFormRequestBody(IList /// Creates an instance given an /// and an instance. @@ -259,7 +257,7 @@ private static void GenerateDefaultResponses(Dictionary GetOperationTags(MethodInfo methodInfo, EndpointMetadat private List GetOpenApiParameters(MethodInfo methodInfo, RoutePattern pattern, bool disableInferredBody) { - var parameters = PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache); + var parameters = PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache.Instance); var openApiParameters = new List(); foreach (var parameter in parameters) @@ -428,12 +426,12 @@ private List GetOpenApiParameters(MethodInfo methodInfo, Route parameter.ParameterType == typeof(HttpResponse) || parameter.ParameterType == typeof(ClaimsPrincipal) || parameter.ParameterType == typeof(CancellationToken) || - ParameterBindingMethodCache.HasBindAsyncMethod(parameter) || + ParameterBindingMethodCache.Instance.HasBindAsyncMethod(parameter) || _serviceProviderIsService?.IsService(parameter.ParameterType) == true) { return (false, null, null); } - else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) + else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType)) { // Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here. if (parameter.Name is { } name && pattern.GetParameter(name) is not null) @@ -452,7 +450,7 @@ private List GetOpenApiParameters(MethodInfo methodInfo, Route else if (disableInferredBody && ( parameter.ParameterType == typeof(string[]) || parameter.ParameterType == typeof(StringValues) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) + (parameter.ParameterType.IsArray && ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) { return (false, ParameterLocation.Query, null); } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs new file mode 100644 index 000000000000..01fe9786c830 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateSlimBuilder(); + +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +app.MapOpenApi(); + +app.MapGet("/", () => "Hello World!"); +app.MapGet("/{name}", (string name) => $"Hello {name}!"); + +return 100; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj new file mode 100644 index 000000000000..410c9c785050 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -0,0 +1,10 @@ + + + + + EnableRequestDelegateGenerator + Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported + + + + diff --git a/src/OpenApi/test/Comparers/OpenApiAnyComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiAnyComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiAnyComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiAnyComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiDiscriminatorComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiDiscriminatorComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiDiscriminatorComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiDiscriminatorComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiExternalDocsComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiExternalDocsComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiExternalDocsComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiExternalDocsComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiReferenceComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiReferenceComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiReferenceComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiReferenceComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiSchemaComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiSchemaComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiSchemaComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiSchemaComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiXmlComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiXmlComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiXmlComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiXmlComparerTests.cs diff --git a/src/OpenApi/test/Extensions/ApiDescriptionExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/ApiDescriptionExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs diff --git a/src/OpenApi/test/Extensions/JsonTypeInfoExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/JsonTypeInfoExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/JsonTypeInfoExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/JsonTypeInfoExtensionsTests.cs diff --git a/src/OpenApi/test/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs diff --git a/src/OpenApi/test/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs diff --git a/src/OpenApi/test/Extensions/OpenApiServiceCollectionExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiServiceCollectionExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/OpenApiServiceCollectionExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiServiceCollectionExtensionsTests.cs diff --git a/src/OpenApi/test/Integration/OpenApiDocumentIntegrationTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs similarity index 100% rename from src/OpenApi/test/Integration/OpenApiDocumentIntegrationTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs diff --git a/src/OpenApi/test/Integration/SampleAppFixture.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/SampleAppFixture.cs similarity index 100% rename from src/OpenApi/test/Integration/SampleAppFixture.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/SampleAppFixture.cs diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests.csproj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj similarity index 100% rename from src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests.csproj rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj diff --git a/src/OpenApi/test/Services/CreateSchemaReferenceIdTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs similarity index 100% rename from src/OpenApi/test/Services/CreateSchemaReferenceIdTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentProviderTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentProviderTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentServiceTestsBase.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentServiceTestsBase.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs diff --git a/src/OpenApi/test/Services/OpenApiGeneratorTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiGeneratorTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs diff --git a/src/OpenApi/test/Shared/SharedTypes.Polymorphism.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.Polymorphism.cs similarity index 100% rename from src/OpenApi/test/Shared/SharedTypes.Polymorphism.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.Polymorphism.cs diff --git a/src/OpenApi/test/Shared/SharedTypes.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.cs similarity index 100% rename from src/OpenApi/test/Shared/SharedTypes.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.cs diff --git a/src/OpenApi/test/Transformers/DocumentTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/DocumentTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/DocumentTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/DocumentTransformerTests.cs diff --git a/src/OpenApi/test/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs diff --git a/src/OpenApi/test/Transformers/OpenApiOptionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OpenApiOptionsTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/OpenApiOptionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OpenApiOptionsTests.cs diff --git a/src/OpenApi/test/Transformers/OperationTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OperationTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/OperationTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OperationTransformerTests.cs diff --git a/src/OpenApi/test/Transformers/SchemaTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/SchemaTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj new file mode 100644 index 000000000000..881ee4782199 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj @@ -0,0 +1,10 @@ + + + + + EnableRequestDelegateGenerator + Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported + + + + diff --git a/src/Shared/ParameterBindingMethodCache.cs b/src/Shared/ParameterBindingMethodCache.cs index d796535c7fab..cac2d2055273 100644 --- a/src/Shared/ParameterBindingMethodCache.cs +++ b/src/Shared/ParameterBindingMethodCache.cs @@ -26,6 +26,12 @@ internal sealed class ParameterBindingMethodCache private static readonly MethodInfo BindAsyncMethod = typeof(ParameterBindingMethodCache).GetMethod(nameof(BindAsync), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo UriTryCreateMethod = typeof(Uri).GetMethod(nameof(Uri.TryCreate), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(UriKind), typeof(Uri).MakeByRefType() })!; + // Thread-safe singletons for ParameterBindingMethodCache + private static readonly Lazy _instance = new(() => new ParameterBindingMethodCache()); + public static ParameterBindingMethodCache Instance = _instance.Value; + private static readonly Lazy _nonThrowingInstance = new(() => new ParameterBindingMethodCache(throwOnInvalidMethod: false)); + public static ParameterBindingMethodCache NonThrowingInstance = _nonThrowingInstance.Value; + // work around https://github.com/dotnet/runtime/issues/81864 by splitting these into a separate class. internal static class SharedExpressions {