diff --git a/src/Http/Http.Abstractions/src/EndpointFilterFactoryContext.cs b/src/Http/Http.Abstractions/src/EndpointFilterFactoryContext.cs
index 86ab54683508..72e2d88f1727 100644
--- a/src/Http/Http.Abstractions/src/EndpointFilterFactoryContext.cs
+++ b/src/Http/Http.Abstractions/src/EndpointFilterFactoryContext.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Reflection;
-using Microsoft.AspNetCore.Builder;
namespace Microsoft.AspNetCore.Http;
@@ -13,34 +12,21 @@ namespace Microsoft.AspNetCore.Http;
public sealed class EndpointFilterFactoryContext
{
///
- /// Creates a new instance of the .
+ /// The associated with the current route handler, or MVC action.
///
- /// The associated with the route handler of the current request.
- /// The associated with the endpoint the filter is targeting.
- /// The instance used to access the application services.
- public EndpointFilterFactoryContext(MethodInfo methodInfo, IList endpointMetadata, IServiceProvider applicationServices)
- {
- ArgumentNullException.ThrowIfNull(methodInfo);
- ArgumentNullException.ThrowIfNull(endpointMetadata);
- ArgumentNullException.ThrowIfNull(applicationServices);
-
- MethodInfo = methodInfo;
- EndpointMetadata = endpointMetadata;
- ApplicationServices = applicationServices;
- }
-
- ///
- /// The associated with the current route handler.
- ///
- public MethodInfo MethodInfo { get; }
-
- ///
- /// The associated with the current endpoint.
- ///
- public IList EndpointMetadata { get; }
+ ///
+ /// In the future this could support more endpoint types.
+ ///
+ public required MethodInfo MethodInfo { get; init; }
///
/// Gets the instance used to access application services.
///
- public IServiceProvider ApplicationServices { get; }
+ public IServiceProvider ApplicationServices { get; init; } = EmptyServiceProvider.Instance;
+
+ private sealed class EmptyServiceProvider : IServiceProvider
+ {
+ public static EmptyServiceProvider Instance { get; } = new EmptyServiceProvider();
+ public object? GetService(Type serviceType) => null;
+ }
}
diff --git a/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs b/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs
index 3afd75bb4091..c767c4efe954 100644
--- a/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs
+++ b/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs
@@ -10,10 +10,12 @@ namespace Microsoft.AspNetCore.Builder;
///
public abstract class EndpointBuilder
{
+ private List>? _filterFactories;
+
///
/// Gets the list of filters that apply to this endpoint.
///
- public IList> FilterFactories { get; } = new List>();
+ public IList> FilterFactories => _filterFactories ??= new();
///
/// Gets or sets the delegate used to process requests for the endpoint.
@@ -33,7 +35,7 @@ public abstract class EndpointBuilder
///
/// Gets the associated with the endpoint.
///
- public IServiceProvider ApplicationServices { get; set; } = EmptyServiceProvider.Instance;
+ public IServiceProvider ApplicationServices { get; init; } = EmptyServiceProvider.Instance;
///
/// Creates an instance of from the .
diff --git a/src/Http/Http.Abstractions/src/Metadata/IEndpointMetadataProvider.cs b/src/Http/Http.Abstractions/src/Metadata/IEndpointMetadataProvider.cs
new file mode 100644
index 000000000000..ec7fa024b090
--- /dev/null
+++ b/src/Http/Http.Abstractions/src/Metadata/IEndpointMetadataProvider.cs
@@ -0,0 +1,26 @@
+// 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;
+using Microsoft.AspNetCore.Builder;
+
+namespace Microsoft.AspNetCore.Http.Metadata;
+
+///
+/// Indicates that a type provides a static method that provides metadata when declared as a parameter type or the
+/// returned type of an route handler delegate.
+///
+public interface IEndpointMetadataProvider
+{
+ ///
+ /// Populates metadata for the related and .
+ ///
+ ///
+ /// This method is called by RequestDelegateFactory when creating a and by MVC when creating endpoints for controller actions.
+ /// This is called for each parameter and return type of the route handler or action with a declared type implementing this interface.
+ /// Add or remove objects on the property of the to modify the being built.
+ ///
+ /// The of the route handler delegate or MVC Action of the endpoint being created.
+ /// The used to construct the endpoint for the given .
+ static abstract void PopulateMetadata(MethodInfo method, EndpointBuilder builder);
+}
diff --git a/src/Http/Http.Abstractions/src/Metadata/IEndpointParameterMetadataProvider.cs b/src/Http/Http.Abstractions/src/Metadata/IEndpointParameterMetadataProvider.cs
new file mode 100644
index 000000000000..2f9a45dac57d
--- /dev/null
+++ b/src/Http/Http.Abstractions/src/Metadata/IEndpointParameterMetadataProvider.cs
@@ -0,0 +1,26 @@
+// 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;
+using Microsoft.AspNetCore.Builder;
+
+namespace Microsoft.AspNetCore.Http.Metadata;
+
+///
+/// Indicates that a type provides a static method that provides metadata when declared as the
+/// parameter type of an route handler delegate.
+///
+public interface IEndpointParameterMetadataProvider
+{
+ ///
+ /// Populates metadata for the related and .
+ ///
+ ///
+ /// This method is called by RequestDelegateFactory when creating a and by MVC when creating endpoints for controller actions.
+ /// This is called for each parameter of the route handler or action with a declared type implementing this interface.
+ /// Add or remove objects on the property of the to modify the being built.
+ ///
+ /// The of the route handler delegate or MVC Action of the endpoint being created.
+ /// The used to construct the endpoint for the given .
+ static abstract void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder);
+}
diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
index c441d489f7e5..121083081390 100644
--- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
+++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
@@ -5,7 +5,7 @@ abstract Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.Arguments.get
abstract Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.GetArgument(int index) -> T
abstract Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
Microsoft.AspNetCore.Builder.EndpointBuilder.ApplicationServices.get -> System.IServiceProvider!
-Microsoft.AspNetCore.Builder.EndpointBuilder.ApplicationServices.set -> void
+Microsoft.AspNetCore.Builder.EndpointBuilder.ApplicationServices.init -> void
Microsoft.AspNetCore.Builder.EndpointBuilder.FilterFactories.get -> System.Collections.Generic.IList!>!
Microsoft.AspNetCore.Builder.IEndpointConventionBuilder.Finally(System.Action! finallyConvention) -> void
Microsoft.AspNetCore.Http.AsParametersAttribute
@@ -16,9 +16,10 @@ Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.DefaultEndpoint
Microsoft.AspNetCore.Http.EndpointFilterDelegate
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.ApplicationServices.get -> System.IServiceProvider!
-Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.EndpointFilterFactoryContext(System.Reflection.MethodInfo! methodInfo, System.Collections.Generic.IList! endpointMetadata, System.IServiceProvider! applicationServices) -> void
-Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.EndpointMetadata.get -> System.Collections.Generic.IList!
+Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.ApplicationServices.init -> void
+Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.EndpointFilterFactoryContext() -> void
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.MethodInfo.get -> System.Reflection.MethodInfo!
+Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.MethodInfo.init -> void
Microsoft.AspNetCore.Http.EndpointFilterInvocationContext
Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.EndpointFilterInvocationContext() -> void
Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object!
@@ -68,6 +69,10 @@ abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string?
Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata
Microsoft.AspNetCore.Http.Metadata.IEndpointDescriptionMetadata
Microsoft.AspNetCore.Http.Metadata.IEndpointDescriptionMetadata.Description.get -> string!
+Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider
+Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider.PopulateMetadata(System.Reflection.MethodInfo! method, Microsoft.AspNetCore.Builder.EndpointBuilder! builder) -> void
+Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider
+Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider.PopulateMetadata(System.Reflection.ParameterInfo! parameter, Microsoft.AspNetCore.Builder.EndpointBuilder! builder) -> void
Microsoft.AspNetCore.Http.Metadata.IEndpointSummaryMetadata
Microsoft.AspNetCore.Http.Metadata.IEndpointSummaryMetadata.Summary.get -> string!
Microsoft.AspNetCore.Mvc.ProblemDetails
diff --git a/src/Http/Http.Extensions/src/EndpointMetadataContext.cs b/src/Http/Http.Extensions/src/EndpointMetadataContext.cs
deleted file mode 100644
index caf96455684c..000000000000
--- a/src/Http/Http.Extensions/src/EndpointMetadataContext.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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;
-
-///
-/// Represents the information accessible during endpoint creation by types that implement .
-///
-public sealed class EndpointMetadataContext
-{
- ///
- /// Creates a new instance of the class.
- ///
- /// The of the route handler delegate of the endpoint being created.
- /// The list of objects that will be added to the metadata of the endpoint.
- /// The instance used to access application services.
- public EndpointMetadataContext(MethodInfo method, IList endpointMetadata, IServiceProvider applicationServices)
- {
- ArgumentNullException.ThrowIfNull(method);
- ArgumentNullException.ThrowIfNull(endpointMetadata);
- ArgumentNullException.ThrowIfNull(applicationServices);
-
- Method = method;
- EndpointMetadata = endpointMetadata;
- ApplicationServices = applicationServices;
- }
-
- ///
- /// Gets the of the route handler delegate of the endpoint being created.
- ///
- public MethodInfo Method { get; }
-
- ///
- /// Gets the list of objects that will be added to the metadata of the endpoint.
- ///
- public IList EndpointMetadata { get; }
-
- ///
- /// Gets the instance used to access application services.
- ///
- public IServiceProvider ApplicationServices { get; }
-}
diff --git a/src/Http/Http.Extensions/src/EndpointParameterMetadataContext.cs b/src/Http/Http.Extensions/src/EndpointParameterMetadataContext.cs
deleted file mode 100644
index 202839cc8080..000000000000
--- a/src/Http/Http.Extensions/src/EndpointParameterMetadataContext.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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;
-
-///
-/// Represents the information accessible during endpoint creation by types that implement .
-///
-public sealed class EndpointParameterMetadataContext
-{
- ///
- /// Creates a new instance of the class.
- ///
- /// The parameter of the route handler delegate of the endpoint being created.
- /// The list of objects that will be added to the metadata of the endpoint.
- /// The instance used to access application services.
- public EndpointParameterMetadataContext(ParameterInfo parameter, IList endpointMetadata, IServiceProvider applicationServices)
- {
- ArgumentNullException.ThrowIfNull(parameter);
- ArgumentNullException.ThrowIfNull(endpointMetadata);
- ArgumentNullException.ThrowIfNull(applicationServices);
-
- Parameter = parameter;
- EndpointMetadata = endpointMetadata;
- ApplicationServices = applicationServices;
- }
-
- ///
- /// Gets the parameter of the route handler delegate of the endpoint being created.
- ///
- public ParameterInfo Parameter { get; }
-
- ///
- /// Gets the list of objects that will be added to the metadata of the endpoint.
- ///
- public IList EndpointMetadata { get; }
-
- ///
- /// Gets the instance used to access application services.
- ///
- public IServiceProvider ApplicationServices { get; }
-}
diff --git a/src/Http/Http.Extensions/src/IEndpointMetadataProvider.cs b/src/Http/Http.Extensions/src/IEndpointMetadataProvider.cs
deleted file mode 100644
index b7bda01e4716..000000000000
--- a/src/Http/Http.Extensions/src/IEndpointMetadataProvider.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.AspNetCore.Http.Metadata;
-
-///
-/// Indicates that a type provides a static method that provides metadata when declared as a parameter type or the
-/// returned type of an route handler delegate.
-///
-public interface IEndpointMetadataProvider
-{
- ///
- /// Populates metadata for the related .
- ///
- ///
- /// This method is called by when creating a .
- /// The property of will contain
- /// the initial metadata for the endpoint.
- /// Add or remove objects on to affect the metadata of the endpoint.
- ///
- /// The .
- static abstract void PopulateMetadata(EndpointMetadataContext context);
-}
diff --git a/src/Http/Http.Extensions/src/IEndpointParameterMetadataProvider.cs b/src/Http/Http.Extensions/src/IEndpointParameterMetadataProvider.cs
deleted file mode 100644
index 45c29dbbf347..000000000000
--- a/src/Http/Http.Extensions/src/IEndpointParameterMetadataProvider.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.AspNetCore.Http.Metadata;
-
-///
-/// Indicates that a type provides a static method that provides metadata when declared as the
-/// parameter type of an route handler delegate.
-///
-public interface IEndpointParameterMetadataProvider
-{
- ///
- /// Populates metadata for the related .
- ///
- ///
- /// This method is called by when creating a .
- /// The property of will contain
- /// the initial metadata for the endpoint.
- /// Add or remove objects on to affect the metadata of the endpoint.
- ///
- /// The .
- static abstract void PopulateMetadata(EndpointParameterMetadataContext parameterContext);
-}
diff --git a/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj b/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj
index ed2fbb631a9b..d0041e24175d 100644
--- a/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj
+++ b/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj
@@ -1,4 +1,4 @@
-
+
ASP.NET Core common extension methods for HTTP abstractions, HTTP headers, HTTP request/response, and session state.
@@ -14,6 +14,7 @@
+
diff --git a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt
index 4b8eccfa6afb..6945deb350e5 100644
--- a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt
+++ b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt
@@ -7,20 +7,6 @@ Microsoft.AspNetCore.Http.HttpValidationProblemDetails (forwarded, contained in
Microsoft.AspNetCore.Http.HttpValidationProblemDetails.Errors.get -> System.Collections.Generic.IDictionary! (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.AspNetCore.Http.HttpValidationProblemDetails.HttpValidationProblemDetails() -> void (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.AspNetCore.Http.HttpValidationProblemDetails.HttpValidationProblemDetails(System.Collections.Generic.IDictionary! errors) -> void (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
-Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext
-Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.ApplicationServices.get -> System.IServiceProvider!
-Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.EndpointMetadataContext(System.Reflection.MethodInfo! method, System.Collections.Generic.IList! endpointMetadata, System.IServiceProvider! applicationServices) -> void
-Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.EndpointMetadata.get -> System.Collections.Generic.IList!
-Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.Method.get -> System.Reflection.MethodInfo!
-Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext
-Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.ApplicationServices.get -> System.IServiceProvider!
-Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.EndpointParameterMetadataContext(System.Reflection.ParameterInfo! parameter, System.Collections.Generic.IList! endpointMetadata, System.IServiceProvider! applicationServices) -> void
-Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.EndpointMetadata.get -> System.Collections.Generic.IList!
-Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.Parameter.get -> System.Reflection.ParameterInfo!
-Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider
-Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider.PopulateMetadata(Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext! context) -> void
-Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider
-Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider.PopulateMetadata(Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext! parameterContext) -> void
Microsoft.AspNetCore.Http.ProblemDetailsOptions
Microsoft.AspNetCore.Http.ProblemDetailsOptions.CustomizeProblemDetails.get -> System.Action?
Microsoft.AspNetCore.Http.ProblemDetailsOptions.CustomizeProblemDetails.set -> void
@@ -38,6 +24,12 @@ Microsoft.AspNetCore.Http.ProblemDetailsOptions.ProblemDetailsOptions() -> void
*REMOVED*Microsoft.AspNetCore.Mvc.ProblemDetails.Title.set -> void
*REMOVED*Microsoft.AspNetCore.Mvc.ProblemDetails.Type.get -> string?
*REMOVED*Microsoft.AspNetCore.Mvc.ProblemDetails.Type.set -> void
+Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointBuilder.get -> Microsoft.AspNetCore.Builder.EndpointBuilder?
+Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointBuilder.init -> void
+Microsoft.AspNetCore.Http.RequestDelegateMetadataResult
+Microsoft.AspNetCore.Http.RequestDelegateMetadataResult.EndpointMetadata.get -> System.Collections.Generic.IReadOnlyList!
+Microsoft.AspNetCore.Http.RequestDelegateMetadataResult.EndpointMetadata.init -> void
+Microsoft.AspNetCore.Http.RequestDelegateMetadataResult.RequestDelegateMetadataResult() -> void
Microsoft.AspNetCore.Mvc.ProblemDetails (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.AspNetCore.Mvc.ProblemDetails.Detail.get -> string? (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.AspNetCore.Mvc.ProblemDetails.Detail.set -> void (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
@@ -52,15 +44,18 @@ Microsoft.AspNetCore.Mvc.ProblemDetails.Title.set -> void (forwarded, contained
Microsoft.AspNetCore.Mvc.ProblemDetails.Type.get -> string? (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.AspNetCore.Mvc.ProblemDetails.Type.set -> void (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.Extensions.DependencyInjection.ProblemDetailsServiceCollectionExtensions
-Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointFilterFactories.get -> System.Collections.Generic.IReadOnlyList!>?
-Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointFilterFactories.init -> void
-Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointMetadata.get -> System.Collections.Generic.IList?
-Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointMetadata.init -> void
Microsoft.Extensions.DependencyInjection.HttpJsonServiceExtensions
static Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(this Microsoft.AspNetCore.Http.HttpRequest! request, System.Type! type, System.Text.Json.Serialization.JsonSerializerContext! context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
static Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(this Microsoft.AspNetCore.Http.HttpRequest! request, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
static Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.WriteAsJsonAsync(this Microsoft.AspNetCore.Http.HttpResponse! response, object? value, System.Type! type, System.Text.Json.Serialization.JsonSerializerContext! context, string? contentType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
static Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.WriteAsJsonAsync(this Microsoft.AspNetCore.Http.HttpResponse! response, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, string? contentType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
+*REMOVED*static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Delegate! handler, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options = null) -> Microsoft.AspNetCore.Http.RequestDelegateResult!
+*REMOVED*static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.Func? targetFactory = null, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options = null) -> Microsoft.AspNetCore.Http.RequestDelegateResult!
+static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Delegate! handler, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options = null, Microsoft.AspNetCore.Http.RequestDelegateMetadataResult? metadataResult = null) -> Microsoft.AspNetCore.Http.RequestDelegateResult!
+static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Delegate! handler, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options) -> Microsoft.AspNetCore.Http.RequestDelegateResult!
+static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.Func? targetFactory = null, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options = null, Microsoft.AspNetCore.Http.RequestDelegateMetadataResult? metadataResult = null) -> Microsoft.AspNetCore.Http.RequestDelegateResult!
+static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.Func? targetFactory, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options) -> Microsoft.AspNetCore.Http.RequestDelegateResult!
+static Microsoft.AspNetCore.Http.RequestDelegateFactory.InferMetadata(System.Reflection.MethodInfo! methodInfo, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions? options = null) -> Microsoft.AspNetCore.Http.RequestDelegateMetadataResult!
static Microsoft.Extensions.DependencyInjection.ProblemDetailsServiceCollectionExtensions.AddProblemDetails(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static Microsoft.Extensions.DependencyInjection.ProblemDetailsServiceCollectionExtensions.AddProblemDetails(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action? configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static Microsoft.Extensions.DependencyInjection.HttpJsonServiceExtensions.ConfigureHttpJsonOptions(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs
index 810d902964c1..9d141f00f658 100644
--- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs
+++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs
@@ -9,10 +9,10 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
@@ -55,8 +55,6 @@ public static partial class RequestDelegateFactory
private static readonly MethodInfo WrapObjectAsValueTaskMethod = typeof(RequestDelegateFactory).GetMethod(nameof(WrapObjectAsValueTask), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo TaskOfTToValueTaskOfObjectMethod = typeof(RequestDelegateFactory).GetMethod(nameof(TaskOfTToValueTaskOfObject), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo ValueTaskOfTToValueTaskOfObjectMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ValueTaskOfTToValueTaskOfObject), BindingFlags.NonPublic | BindingFlags.Static)!;
- private static readonly MethodInfo PopulateMetadataForParameterMethod = typeof(RequestDelegateFactory).GetMethod(nameof(PopulateMetadataForParameter), BindingFlags.NonPublic | BindingFlags.Static)!;
- private static readonly MethodInfo PopulateMetadataForEndpointMethod = typeof(RequestDelegateFactory).GetMethod(nameof(PopulateMetadataForEndpoint), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo ArrayEmptyOfObjectMethod = typeof(Array).GetMethod(nameof(Array.Empty), BindingFlags.Public | BindingFlags.Static)!.MakeGenericMethod(new Type[] { typeof(object) });
private static readonly PropertyInfo QueryIndexerProperty = typeof(IQueryCollection).GetProperty("Item")!;
@@ -115,14 +113,52 @@ public static partial class RequestDelegateFactory
private static readonly string[] DefaultAcceptsContentType = new[] { "application/json" };
private static readonly string[] FormFileContentType = new[] { "multipart/form-data" };
+ ///
+ /// Returns metadata inferred automatically for the created by .
+ /// This includes metadata inferred by and implemented by parameter and return types to the .
+ ///
+ /// The for the route handler to be passed to .
+ /// The options that will be used when calling .
+ /// The to be passed to .
+ [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")]
+ public static RequestDelegateMetadataResult InferMetadata(MethodInfo methodInfo, RequestDelegateFactoryOptions? options = null)
+ {
+ var factoryContext = CreateFactoryContext(options);
+ factoryContext.ArgumentExpressions = CreateArgumentsAndInferMetadata(methodInfo, factoryContext);
+
+ return new RequestDelegateMetadataResult
+ {
+ EndpointMetadata = AsReadOnlyList(factoryContext.EndpointBuilder.Metadata),
+ };
+ }
+
+ ///
+ /// Creates a implementation for .
+ ///
+ /// A request handler with any number of custom parameters that often produces a response with its return value.
+ /// The used to configure the behavior of the handler.
+ /// The .
+ [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")]
+ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options)
+ {
+ return Create(handler, options, metadataResult: null);
+ }
+
///
/// Creates a implementation for .
///
/// A request handler with any number of custom parameters that often produces a response with its return value.
/// The used to configure the behavior of the handler.
+ ///
+ /// The result returned from if that was used to inferring metadata before creating the final RequestDelegate.
+ /// If , this call to method will infer the metadata that
+ /// would have inferred for the same and populate
+ /// with that metadata. Otherwise, this metadata inference will be skipped as this step has already been done.
+ ///
/// The .
[RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")]
- public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options = null)
+ [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "Required to maintain compatibility")]
+ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options = null, RequestDelegateMetadataResult? metadataResult = null)
{
if (handler is null)
{
@@ -135,20 +171,20 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact
null => null,
};
- var factoryContext = CreateFactoryContext(options, handler);
+ var factoryContext = CreateFactoryContext(options, metadataResult, handler);
Expression> targetFactory = (httpContext) => handler.Target;
-
var targetableRequestDelegate = CreateTargetableRequestDelegate(handler.Method, targetExpression, factoryContext, targetFactory);
- if (targetableRequestDelegate is null)
+ RequestDelegate finalRequestDelegate = targetableRequestDelegate switch
{
// handler is a RequestDelegate that has not been modified by a filter. Short-circuit and return the original RequestDelegate back.
// It's possible a filter factory has still modified the endpoint metadata though.
- return new RequestDelegateResult((RequestDelegate)handler, AsReadOnlyList(factoryContext.Metadata));
- }
+ null => (RequestDelegate)handler,
+ _ => httpContext => targetableRequestDelegate(handler.Target, httpContext),
+ };
- return new RequestDelegateResult(httpContext => targetableRequestDelegate(handler.Target, httpContext), AsReadOnlyList(factoryContext.Metadata));
+ return CreateRequestDelegateResult(finalRequestDelegate, factoryContext.EndpointBuilder);
}
///
@@ -158,9 +194,28 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact
/// Creates the for the non-static method.
/// The used to configure the behavior of the handler.
/// The .
+ [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")]
+ public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory, RequestDelegateFactoryOptions? options)
+ {
+ return Create(methodInfo, targetFactory, options, metadataResult: null);
+ }
+ ///
+ /// Creates a implementation for .
+ ///
+ /// A request handler with any number of custom parameters that often produces a response with its return value.
+ /// Creates the for the non-static method.
+ /// The used to configure the behavior of the handler.
+ ///
+ /// The result returned from if that was used to inferring metadata before creating the final RequestDelegate.
+ /// If , this call to method will infer the metadata that
+ /// would have inferred for the same and populate
+ /// with that metadata. Otherwise, this metadata inference will be skipped as this step has already been done.
+ ///
+ /// The .
[RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")]
- public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory = null, RequestDelegateFactoryOptions? options = null)
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory = null, RequestDelegateFactoryOptions? options = null, RequestDelegateMetadataResult? metadataResult = null)
{
if (methodInfo is null)
{
@@ -172,45 +227,66 @@ public static RequestDelegateResult Create(MethodInfo methodInfo, Func untargetableRequestDelegate(null, httpContext), AsReadOnlyList(factoryContext.Metadata));
- }
-
- targetFactory = context => Activator.CreateInstance(methodInfo.DeclaringType)!;
+ finalRequestDelegate = httpContext => untargetableRequestDelegate(null, httpContext);
}
+ else
+ {
+ targetFactory ??= context => Activator.CreateInstance(methodInfo.DeclaringType)!;
- var targetExpression = Expression.Convert(TargetExpr, methodInfo.DeclaringType);
- var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression, factoryContext, context => targetFactory(context));
+ var targetExpression = Expression.Convert(TargetExpr, methodInfo.DeclaringType);
+ var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression, factoryContext, context => targetFactory(context));
- // CreateTargetableRequestDelegate can only return null given a RequestDelegate passed into the other RDF.Create() overload.
- Debug.Assert(targetableRequestDelegate is not null);
+ // CreateTargetableRequestDelegate can only return null given a RequestDelegate passed into the other RDF.Create() overload.
+ Debug.Assert(targetableRequestDelegate is not null);
- return new RequestDelegateResult(httpContext => targetableRequestDelegate(targetFactory(httpContext), httpContext), AsReadOnlyList(factoryContext.Metadata));
+ finalRequestDelegate = httpContext => targetableRequestDelegate(targetFactory(httpContext), httpContext);
+ }
+
+ return CreateRequestDelegateResult(finalRequestDelegate, factoryContext.EndpointBuilder);
}
- private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions? options, Delegate? handler = null)
+ private static RequestDelegateFactoryContext CreateFactoryContext(RequestDelegateFactoryOptions? options, RequestDelegateMetadataResult? metadataResult = null, Delegate? handler = null)
{
- return new FactoryContext
+ if (metadataResult?.CachedFactoryContext is not null)
+ {
+ metadataResult.CachedFactoryContext.MetadataAlreadyInferred = true;
+ // The handler was not passed in to the InferMetadata call that originally created this context.
+ metadataResult.CachedFactoryContext.Handler = handler;
+ return metadataResult.CachedFactoryContext;
+ }
+
+ var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices ?? EmptyServiceProvider.Instance;
+ var endpointBuilder = options?.EndpointBuilder ?? new RDFEndpointBuilder(serviceProvider);
+
+ var factoryContext = new RequestDelegateFactoryContext
{
Handler = handler,
- ServiceProvider = options?.ServiceProvider,
- ServiceProviderIsService = options?.ServiceProvider?.GetService(),
+ ServiceProvider = serviceProvider,
+ ServiceProviderIsService = serviceProvider.GetService(),
RouteParameters = options?.RouteParameterNames?.ToList(),
ThrowOnBadRequest = options?.ThrowOnBadRequest ?? false,
DisableInferredFromBody = options?.DisableInferBodyFromParameters ?? false,
- FilterFactories = options?.EndpointFilterFactories?.ToList(),
- Metadata = options?.EndpointMetadata ?? new List(),
+ EndpointBuilder = endpointBuilder,
+ MetadataAlreadyInferred = metadataResult is not null,
};
+
+ return factoryContext;
+ }
+
+ private static RequestDelegateResult CreateRequestDelegateResult(RequestDelegate finalRequestDelegate, EndpointBuilder endpointBuilder)
+ {
+ endpointBuilder.RequestDelegate = finalRequestDelegate;
+ return new RequestDelegateResult(finalRequestDelegate, AsReadOnlyList(endpointBuilder.Metadata));
}
private static IReadOnlyList AsReadOnlyList(IList metadata)
@@ -223,7 +299,11 @@ private static IReadOnlyList AsReadOnlyList(IList metadata)
return new List(metadata);
}
- private static Func? CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext, Expression>? targetFactory = null)
+ private static Func? CreateTargetableRequestDelegate(
+ MethodInfo methodInfo,
+ Expression? targetExpression,
+ RequestDelegateFactoryContext factoryContext,
+ Expression>? targetFactory = null)
{
// Non void return type
@@ -241,22 +321,18 @@ private static IReadOnlyList AsReadOnlyList(IList metadata)
// return default;
// }
- // CreateArguments will add metadata inferred from parameter details
- var arguments = CreateArguments(methodInfo.GetParameters(), factoryContext);
- var returnType = methodInfo.ReturnType;
- factoryContext.MethodCall = CreateMethodCall(methodInfo, targetExpression, arguments);
-
- // Add metadata provided by the delegate return type and parameter types next, this will be more specific than inferred metadata from above
- AddTypeProvidedMetadata(methodInfo,
- factoryContext.Metadata,
- factoryContext.ServiceProvider,
- CollectionsMarshal.AsSpan(factoryContext.Parameters));
+ // If ArgumentExpressions is not null here, it's guaranteed we have already inferred metadata and we can reuse a lot of work.
+ // The converse is not true. Metadata may have already been inferred even if ArgumentExpressions is null, but metadata
+ // inference is skipped internally if necessary.
+ factoryContext.ArgumentExpressions ??= CreateArgumentsAndInferMetadata(methodInfo, factoryContext);
+ factoryContext.MethodCall = CreateMethodCall(methodInfo, targetExpression, factoryContext.ArgumentExpressions);
EndpointFilterDelegate? filterPipeline = null;
+ var returnType = methodInfo.ReturnType;
// If there are filters registered on the route handler, then we update the method call and
// return type associated with the request to allow for the filter invocation pipeline.
- if (factoryContext.FilterFactories is { Count: > 0 })
+ if (factoryContext.EndpointBuilder.FilterFactories.Count > 0)
{
filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext, targetFactory);
@@ -270,7 +346,7 @@ private static IReadOnlyList AsReadOnlyList(IList metadata)
new[] { InvokedFilterContextExpr },
Expression.Assign(
InvokedFilterContextExpr,
- CreateEndpointFilterInvocationContextBase(factoryContext)),
+ CreateEndpointFilterInvocationContextBase(factoryContext, factoryContext.ArgumentExpressions)),
Expression.Invoke(invokePipeline, InvokedFilterContextExpr)
);
}
@@ -279,11 +355,7 @@ private static IReadOnlyList AsReadOnlyList(IList metadata)
// return null for plain RequestDelegates that have not been modified by filters so we can just pass back the original RequestDelegate.
if (filterPipeline is null && factoryContext.Handler is RequestDelegate)
{
- // Make sure we're still not handling a return value.
- if (!returnType.IsGenericType || returnType.GetGenericTypeDefinition() != typeof(Task<>))
- {
- return null;
- }
+ return null;
}
var responseWritingMethodCall = factoryContext.ParamCheckExpressions.Count > 0 ?
@@ -298,9 +370,26 @@ private static IReadOnlyList AsReadOnlyList(IList metadata)
return HandleRequestBodyAndCompileRequestDelegate(responseWritingMethodCall, factoryContext);
}
- private static EndpointFilterDelegate? CreateFilterPipeline(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext, Expression>? targetFactory)
+ private static Expression[] CreateArgumentsAndInferMetadata(MethodInfo methodInfo, RequestDelegateFactoryContext factoryContext)
+ {
+ // Add any default accepts metadata. This does a lot of reflection and expression tree building, so the results are cached in RequestDelegateFactoryOptions.FactoryContext
+ // For later reuse in Create().
+ var args = CreateArguments(methodInfo.GetParameters(), factoryContext);
+
+ if (!factoryContext.MetadataAlreadyInferred)
+ {
+ // Add metadata provided by the delegate return type and parameter types next, this will be more specific than inferred metadata from above
+ EndpointMetadataPopulator.PopulateMetadata(methodInfo,
+ factoryContext.EndpointBuilder,
+ factoryContext.Parameters);
+ }
+
+ return args;
+ }
+
+ private static EndpointFilterDelegate? CreateFilterPipeline(MethodInfo methodInfo, Expression? targetExpression, RequestDelegateFactoryContext factoryContext, Expression>? targetFactory)
{
- Debug.Assert(factoryContext.FilterFactories is not null);
+ Debug.Assert(factoryContext.EndpointBuilder.FilterFactories.Count > 0);
// httpContext.Response.StatusCode >= 400
// ? Task.CompletedTask
// : {
@@ -339,16 +428,17 @@ targetExpression is null
CompletedValueTaskExpr,
handlerInvocation),
FilterContextExpr).Compile();
- var routeHandlerContext = new EndpointFilterFactoryContext(
- methodInfo,
- factoryContext.Metadata,
- factoryContext.ServiceProvider ?? EmptyServiceProvider.Instance);
+ var routeHandlerContext = new EndpointFilterFactoryContext
+ {
+ MethodInfo = methodInfo,
+ ApplicationServices = factoryContext.EndpointBuilder.ApplicationServices,
+ };
var initialFilteredInvocation = filteredInvocation;
- for (var i = factoryContext.FilterFactories.Count - 1; i >= 0; i--)
+ for (var i = factoryContext.EndpointBuilder.FilterFactories.Count - 1; i >= 0; i--)
{
- var currentFilterFactory = factoryContext.FilterFactories[i];
+ var currentFilterFactory = factoryContext.EndpointBuilder.FilterFactories[i];
filteredInvocation = currentFilterFactory(routeHandlerContext, filteredInvocation);
}
@@ -428,7 +518,7 @@ private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall,
return ExecuteAwaited(task);
}
- private static Expression CreateEndpointFilterInvocationContextBase(FactoryContext factoryContext)
+ private static Expression CreateEndpointFilterInvocationContextBase(RequestDelegateFactoryContext factoryContext, Expression[] arguments)
{
// In the event that a constructor matching the arity of the
// provided parameters is not found, we fall back to using the
@@ -447,9 +537,9 @@ private static Expression CreateEndpointFilterInvocationContextBase(FactoryConte
return fallbackConstruction;
}
- var arguments = new Expression[factoryContext.ArgumentExpressions.Length + 1];
- arguments[0] = HttpContextExpr;
- factoryContext.ArgumentExpressions.CopyTo(arguments, 1);
+ var expandedArguments = new Expression[arguments.Length + 1];
+ expandedArguments[0] = HttpContextExpr;
+ arguments.CopyTo(expandedArguments, 1);
var constructorType = factoryContext.ArgumentTypes?.Length switch
{
@@ -476,69 +566,14 @@ private static Expression CreateEndpointFilterInvocationContextBase(FactoryConte
}
// new EndpointFilterInvocationContext(httpContext, name_local, int_local);
- return Expression.New(constructor, arguments);
+ return Expression.New(constructor, expandedArguments);
}
// new EndpointFilterInvocationContext(httpContext, (object)name_local, (object)int_local);
return fallbackConstruction;
}
- private static void AddTypeProvidedMetadata(MethodInfo methodInfo, IList metadata, IServiceProvider? services, ReadOnlySpan parameters)
- {
- object?[]? invokeArgs = null;
-
- // Get metadata from parameter types
- foreach (var parameter in parameters)
- {
- if (typeof(IEndpointParameterMetadataProvider).IsAssignableFrom(parameter.ParameterType))
- {
- // Parameter type implements IEndpointParameterMetadataProvider
- var parameterContext = new EndpointParameterMetadataContext(parameter, metadata, services ?? EmptyServiceProvider.Instance);
- invokeArgs ??= new object[1];
- invokeArgs[0] = parameterContext;
- PopulateMetadataForParameterMethod.MakeGenericMethod(parameter.ParameterType).Invoke(null, invokeArgs);
- }
-
- if (typeof(IEndpointMetadataProvider).IsAssignableFrom(parameter.ParameterType))
- {
- // Parameter type implements IEndpointMetadataProvider
- var context = new EndpointMetadataContext(methodInfo, metadata, services ?? EmptyServiceProvider.Instance);
- invokeArgs ??= new object[1];
- invokeArgs[0] = context;
- PopulateMetadataForEndpointMethod.MakeGenericMethod(parameter.ParameterType).Invoke(null, invokeArgs);
- }
- }
-
- // Get metadata from return type
- var returnType = methodInfo.ReturnType;
- if (AwaitableInfo.IsTypeAwaitable(returnType, out var awaitableInfo))
- {
- returnType = awaitableInfo.ResultType;
- }
-
- if (returnType is not null && typeof(IEndpointMetadataProvider).IsAssignableFrom(returnType))
- {
- // Return type implements IEndpointMetadataProvider
- var context = new EndpointMetadataContext(methodInfo, metadata, services ?? EmptyServiceProvider.Instance);
- invokeArgs ??= new object[1];
- invokeArgs[0] = context;
- PopulateMetadataForEndpointMethod.MakeGenericMethod(returnType).Invoke(null, invokeArgs);
- }
- }
-
- private static void PopulateMetadataForParameter(EndpointParameterMetadataContext parameterContext)
- where T : IEndpointParameterMetadataProvider
- {
- T.PopulateMetadata(parameterContext);
- }
-
- private static void PopulateMetadataForEndpoint(EndpointMetadataContext context)
- where T : IEndpointMetadataProvider
- {
- T.PopulateMetadata(context);
- }
-
- private static Expression[] CreateArguments(ParameterInfo[]? parameters, FactoryContext factoryContext)
+ private static Expression[] CreateArguments(ParameterInfo[]? parameters, RequestDelegateFactoryContext factoryContext)
{
if (parameters is null || parameters.Length == 0)
{
@@ -548,11 +583,10 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory
var args = new Expression[parameters.Length];
factoryContext.ArgumentTypes = new Type[parameters.Length];
- factoryContext.ArgumentExpressions = new Expression[parameters.Length];
factoryContext.BoxedArgs = new Expression[parameters.Length];
factoryContext.Parameters = new List(parameters);
- var hasFilters = factoryContext.FilterFactories is { Count: > 0 };
+ var hasFilters = factoryContext.EndpointBuilder.FilterFactories.Count > 0;
for (var i = 0; i < parameters.Length; i++)
{
@@ -580,7 +614,6 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory
}
factoryContext.ArgumentTypes[i] = parameters[i].ParameterType;
- factoryContext.ArgumentExpressions[i] = args[i];
factoryContext.BoxedArgs[i] = Expression.Convert(args[i], typeof(object));
}
@@ -605,7 +638,7 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory
return args;
}
- private static Expression CreateArgument(ParameterInfo parameter, FactoryContext factoryContext)
+ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext)
{
if (parameter.Name is null)
{
@@ -790,7 +823,7 @@ target is null ?
// If we're calling TryParse or validating parameter optionality and
// wasParamCheckFailure indicates it failed, set a 400 StatusCode instead of calling the method.
- private static Expression CreateParamCheckingResponseWritingMethodCall(Type returnType, FactoryContext factoryContext)
+ private static Expression CreateParamCheckingResponseWritingMethodCall(Type returnType, RequestDelegateFactoryContext factoryContext)
{
// {
// string tempSourceString;
@@ -842,7 +875,7 @@ private static Expression CreateParamCheckingResponseWritingMethodCall(Type retu
// If filters have been registered, we set the `wasParamCheckFailure` property
// but do not return from the invocation to allow the filters to run.
- if (factoryContext.FilterFactories is { Count: > 0 })
+ if (factoryContext.EndpointBuilder.FilterFactories.Count > 0)
{
// if (wasParamCheckFailure)
// {
@@ -999,7 +1032,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
}
}
- private static Func HandleRequestBodyAndCompileRequestDelegate(Expression responseWritingMethodCall, FactoryContext factoryContext)
+ private static Func HandleRequestBodyAndCompileRequestDelegate(Expression responseWritingMethodCall, RequestDelegateFactoryContext factoryContext)
{
if (factoryContext.JsonRequestBodyParameter is null && !factoryContext.ReadForm)
{
@@ -1040,7 +1073,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
}
}
- private static Func HandleRequestBodyAndCompileRequestDelegateForJson(Expression responseWritingMethodCall, FactoryContext factoryContext)
+ private static Func HandleRequestBodyAndCompileRequestDelegateForJson(Expression responseWritingMethodCall, RequestDelegateFactoryContext factoryContext)
{
Debug.Assert(factoryContext.JsonRequestBodyParameter is not null, "factoryContext.JsonRequestBodyParameter is null for a JSON body.");
@@ -1164,7 +1197,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
private static Func HandleRequestBodyAndCompileRequestDelegateForForm(
Expression responseWritingMethodCall,
- FactoryContext factoryContext)
+ RequestDelegateFactoryContext factoryContext)
{
Debug.Assert(factoryContext.FirstFormRequestBodyParameter is not null, "factoryContext.FirstFormRequestBodyParameter is null for a form body.");
@@ -1278,7 +1311,7 @@ private static Expression GetValueFromProperty(MemberExpression sourceExpression
return Expression.Convert(indexExpression, returnType ?? typeof(string));
}
- private static Expression BindParameterFromProperties(ParameterInfo parameter, FactoryContext factoryContext)
+ private static Expression BindParameterFromProperties(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext)
{
var parameterType = parameter.ParameterType;
var isNullable = Nullable.GetUnderlyingType(parameterType) != null ||
@@ -1348,7 +1381,7 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, F
return argumentExpression;
}
- private static Expression BindParameterFromService(ParameterInfo parameter, FactoryContext factoryContext)
+ private static Expression BindParameterFromService(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext)
{
var isOptional = IsOptionalParameter(parameter, factoryContext);
@@ -1359,7 +1392,7 @@ private static Expression BindParameterFromService(ParameterInfo parameter, Fact
return Expression.Call(GetRequiredServiceMethod.MakeGenericMethod(parameter.ParameterType), RequestServicesExpr);
}
- private static Expression BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, FactoryContext factoryContext, string source)
+ private static Expression BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, RequestDelegateFactoryContext factoryContext, string source)
{
if (parameter.ParameterType == typeof(string) || parameter.ParameterType == typeof(string[])
|| parameter.ParameterType == typeof(StringValues) || parameter.ParameterType == typeof(StringValues?))
@@ -1589,7 +1622,7 @@ private static Expression BindParameterFromValue(ParameterInfo parameter, Expres
private static Expression BindParameterFromExpression(
ParameterInfo parameter,
Expression valueExpression,
- FactoryContext factoryContext,
+ RequestDelegateFactoryContext factoryContext,
string source)
{
var nullability = factoryContext.NullabilityContext.Create(parameter);
@@ -1658,7 +1691,7 @@ private static Expression BindParameterFromExpression(
Expression.Convert(Expression.Constant(parameter.DefaultValue), parameter.ParameterType)));
}
- private static Expression BindParameterFromProperty(ParameterInfo parameter, MemberExpression property, PropertyInfo itemProperty, string key, FactoryContext factoryContext, string source) =>
+ private static Expression BindParameterFromProperty(ParameterInfo parameter, MemberExpression property, PropertyInfo itemProperty, string key, RequestDelegateFactoryContext factoryContext, string source) =>
BindParameterFromValue(parameter, GetValueFromProperty(property, itemProperty, key, GetExpressionType(parameter.ParameterType)), factoryContext, source);
private static Type? GetExpressionType(Type type) =>
@@ -1667,14 +1700,14 @@ private static Expression BindParameterFromProperty(ParameterInfo parameter, Mem
type == typeof(StringValues?) ? typeof(StringValues?) :
null;
- private static Expression BindParameterFromRouteValueOrQueryString(ParameterInfo parameter, string key, FactoryContext factoryContext)
+ private static Expression BindParameterFromRouteValueOrQueryString(ParameterInfo parameter, string key, RequestDelegateFactoryContext factoryContext)
{
var routeValue = GetValueFromProperty(RouteValuesExpr, RouteValuesIndexerProperty, key);
var queryValue = GetValueFromProperty(QueryExpr, QueryIndexerProperty, key);
return BindParameterFromValue(parameter, Expression.Coalesce(routeValue, queryValue), factoryContext, "route or query string");
}
- private static Expression BindParameterFromBindAsync(ParameterInfo parameter, FactoryContext factoryContext)
+ private static Expression BindParameterFromBindAsync(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext)
{
// We reference the boundValues array by parameter index here
var isOptional = IsOptionalParameter(parameter, factoryContext);
@@ -1717,17 +1750,19 @@ private static Expression BindParameterFromBindAsync(ParameterInfo parameter, Fa
return Expression.Convert(boundValueExpr, parameter.ParameterType);
}
- private static void InsertInferredAcceptsMetadata(FactoryContext factoryContext, Type type, string[] contentTypes)
+ private static void AddInferredAcceptsMetadata(RequestDelegateFactoryContext factoryContext, Type type, string[] contentTypes)
{
- // Insert the automatically-inferred AcceptsMetadata at the beginning of the list to give it the lowest precedence.
- // It really doesn't makes sense for this metadata to be overridden, but we're preserving the old behavior out of an abundance of caution.
- // I suspect most filters and metadata providers will just add their metadata to the end of the list.
- factoryContext.Metadata.Insert(0, new AcceptsMetadata(type, factoryContext.AllowEmptyRequestBody, contentTypes));
+ if (factoryContext.MetadataAlreadyInferred)
+ {
+ return;
+ }
+
+ factoryContext.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type, factoryContext.AllowEmptyRequestBody, contentTypes));
}
private static Expression BindParameterFromFormFiles(
ParameterInfo parameter,
- FactoryContext factoryContext)
+ RequestDelegateFactoryContext factoryContext)
{
if (factoryContext.FirstFormRequestBodyParameter is null)
{
@@ -1739,7 +1774,7 @@ private static Expression BindParameterFromFormFiles(
// Do not duplicate the metadata if there are multiple form parameters
if (!factoryContext.ReadForm)
{
- InsertInferredAcceptsMetadata(factoryContext, parameter.ParameterType, FormFileContentType);
+ AddInferredAcceptsMetadata(factoryContext, parameter.ParameterType, FormFileContentType);
}
factoryContext.ReadForm = true;
@@ -1750,7 +1785,7 @@ private static Expression BindParameterFromFormFiles(
private static Expression BindParameterFromFormFile(
ParameterInfo parameter,
string key,
- FactoryContext factoryContext,
+ RequestDelegateFactoryContext factoryContext,
string trackedParameterSource)
{
if (factoryContext.FirstFormRequestBodyParameter is null)
@@ -1763,7 +1798,7 @@ private static Expression BindParameterFromFormFile(
// Do not duplicate the metadata if there are multiple form parameters
if (!factoryContext.ReadForm)
{
- InsertInferredAcceptsMetadata(factoryContext, parameter.ParameterType, FormFileContentType);
+ AddInferredAcceptsMetadata(factoryContext, parameter.ParameterType, FormFileContentType);
}
factoryContext.ReadForm = true;
@@ -1773,7 +1808,7 @@ private static Expression BindParameterFromFormFile(
return BindParameterFromExpression(parameter, valueExpression, factoryContext, "form file");
}
- private static Expression BindParameterFromBody(ParameterInfo parameter, bool allowEmpty, FactoryContext factoryContext)
+ private static Expression BindParameterFromBody(ParameterInfo parameter, bool allowEmpty, RequestDelegateFactoryContext factoryContext)
{
if (factoryContext.JsonRequestBodyParameter is not null)
{
@@ -1793,7 +1828,7 @@ private static Expression BindParameterFromBody(ParameterInfo parameter, bool al
factoryContext.JsonRequestBodyParameter = parameter;
factoryContext.AllowEmptyRequestBody = allowEmpty || isOptional;
- InsertInferredAcceptsMetadata(factoryContext, parameter.ParameterType, DefaultAcceptsContentType);
+ AddInferredAcceptsMetadata(factoryContext, parameter.ParameterType, DefaultAcceptsContentType);
if (!factoryContext.AllowEmptyRequestBody)
{
@@ -1857,7 +1892,7 @@ private static Expression BindParameterFromBody(ParameterInfo parameter, bool al
return Expression.Convert(BodyValueExpr, parameter.ParameterType);
}
- private static bool IsOptionalParameter(ParameterInfo parameter, FactoryContext factoryContext)
+ private static bool IsOptionalParameter(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext)
{
if (parameter is PropertyAsParameterInfo argument)
{
@@ -2096,50 +2131,6 @@ private static async Task ExecuteResultWriteResponse(IResult? result, HttpContex
{
await EnsureRequestResultNotNull(result).ExecuteAsync(httpContext);
}
-
- private sealed class FactoryContext
- {
- // Options
- // Handler could be null if the MethodInfo overload of RDF.Create is used, but that doesn't matter because this is
- // only referenced to optimize certain cases where a RequestDelegate is the handler and filters don't modify it.
- public Delegate? Handler { get; init; }
- public IServiceProvider? ServiceProvider { get; init; }
- public IServiceProviderIsService? ServiceProviderIsService { get; init; }
- public List? RouteParameters { get; init; }
- public bool ThrowOnBadRequest { get; init; }
- public bool DisableInferredFromBody { get; init; }
-
- // Temporary State
- public ParameterInfo? JsonRequestBodyParameter { get; set; }
- public bool AllowEmptyRequestBody { get; set; }
-
- public bool UsingTempSourceString { get; set; }
- public List ExtraLocals { get; } = new();
- public List ParamCheckExpressions { get; } = new();
- public List>> ParameterBinders { get; } = new();
-
- public Dictionary TrackedParameters { get; } = new();
- public bool HasMultipleBodyParameters { get; set; }
- public bool HasInferredBody { get; set; }
-
- public IList Metadata { get; init; } = default!;
-
- public NullabilityInfoContext NullabilityContext { get; } = new();
-
- public bool ReadForm { get; set; }
- public ParameterInfo? FirstFormRequestBodyParameter { get; set; }
- // Properties for constructing and managing filters
- public List ContextArgAccess { get; } = new();
- public Expression? MethodCall { get; set; }
- public Type[] ArgumentTypes { get; set; } = Array.Empty();
- public Expression[] ArgumentExpressions { get; set; } = Array.Empty();
- public Expression[] BoxedArgs { get; set; } = Array.Empty();
- public List>? FilterFactories { get; init; }
- public bool FilterFactoriesHaveRunWithoutModifyingPerRequestBehavior { get; set; }
-
- public List Parameters { get; set; } = new();
- }
-
private static class RequestDelegateFactoryConstants
{
public const string RouteAttribute = "Route (Attribute)";
@@ -2324,7 +2315,7 @@ private static void SetPlaintextContentType(HttpContext httpContext)
httpContext.Response.ContentType ??= "text/plain; charset=utf-8";
}
- private static string BuildErrorMessageForMultipleBodyParameters(FactoryContext factoryContext)
+ private static string BuildErrorMessageForMultipleBodyParameters(RequestDelegateFactoryContext factoryContext)
{
var errorMessage = new StringBuilder();
errorMessage.AppendLine("Failure to infer one or more parameters.");
@@ -2341,7 +2332,7 @@ private static string BuildErrorMessageForMultipleBodyParameters(FactoryContext
return errorMessage.ToString();
}
- private static string BuildErrorMessageForInferredBodyParameter(FactoryContext factoryContext)
+ private static string BuildErrorMessageForInferredBodyParameter(RequestDelegateFactoryContext factoryContext)
{
var errorMessage = new StringBuilder();
errorMessage.AppendLine("Body was inferred but the method does not allow inferred body parameters.");
@@ -2358,7 +2349,7 @@ private static string BuildErrorMessageForInferredBodyParameter(FactoryContext f
return errorMessage.ToString();
}
- private static string BuildErrorMessageForFormAndJsonBodyParameters(FactoryContext factoryContext)
+ private static string BuildErrorMessageForFormAndJsonBodyParameters(RequestDelegateFactoryContext factoryContext)
{
var errorMessage = new StringBuilder();
errorMessage.AppendLine("An action cannot use both form and JSON body parameters.");
@@ -2372,7 +2363,7 @@ private static string BuildErrorMessageForFormAndJsonBodyParameters(FactoryConte
return errorMessage.ToString();
}
- private static void FormatTrackedParameters(FactoryContext factoryContext, StringBuilder errorMessage)
+ private static void FormatTrackedParameters(RequestDelegateFactoryContext factoryContext, StringBuilder errorMessage)
{
foreach (var kv in factoryContext.TrackedParameters)
{
@@ -2398,10 +2389,22 @@ public Task ExecuteAsync(HttpContext httpContext)
}
}
- private sealed class EmptyServiceProvider : IServiceProvider
+ private sealed class RDFEndpointBuilder : EndpointBuilder
{
- public static IServiceProvider Instance { get; } = new EmptyServiceProvider();
+ public RDFEndpointBuilder(IServiceProvider applicationServices)
+ {
+ ApplicationServices = applicationServices;
+ }
+ public override Endpoint Build()
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ private sealed class EmptyServiceProvider : IServiceProvider
+ {
+ public static EmptyServiceProvider Instance { get; } = new EmptyServiceProvider();
public object? GetService(Type serviceType) => null;
}
}
diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactoryContext.cs b/src/Http/Http.Extensions/src/RequestDelegateFactoryContext.cs
new file mode 100644
index 000000000000..65b9d7b0f964
--- /dev/null
+++ b/src/Http/Http.Extensions/src/RequestDelegateFactoryContext.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Http;
+
+internal sealed class RequestDelegateFactoryContext
+{
+ // Options
+ public required IServiceProvider ServiceProvider { get; init; }
+ public required IServiceProviderIsService? ServiceProviderIsService { get; init; }
+ public required List? RouteParameters { get; init; }
+ public required bool ThrowOnBadRequest { get; init; }
+ public required bool DisableInferredFromBody { get; init; }
+ public required EndpointBuilder EndpointBuilder { get; init; }
+
+ // Handler could be null if the MethodInfo overload of RDF.Create is used, but that doesn't matter because this is
+ // only referenced to optimize certain cases where a RequestDelegate is the handler and filters don't modify it.
+ public Delegate? Handler { get; set; }
+
+ // Temporary State
+
+ // This indicates whether we're currently in RDF.Create() with a non-null RequestDelegateResult.
+ // This is settable, because if this context is cached we need to set it to true after it's created.
+ // But it's still possible this should be initialized to true initially, so we're making it required.
+ // In theory, someone could construct their own RequestDelegateResult without a cached context.
+ public required bool MetadataAlreadyInferred { get; set; }
+
+ public ParameterInfo? JsonRequestBodyParameter { get; set; }
+ public bool AllowEmptyRequestBody { get; set; }
+
+ public bool UsingTempSourceString { get; set; }
+ public List ExtraLocals { get; } = new();
+ public List ParamCheckExpressions { get; } = new();
+ public List>> ParameterBinders { get; } = new();
+
+ public Dictionary TrackedParameters { get; } = new();
+ public bool HasMultipleBodyParameters { get; set; }
+ public bool HasInferredBody { get; set; }
+
+ public NullabilityInfoContext NullabilityContext { get; } = new();
+
+ public bool ReadForm { get; set; }
+ public ParameterInfo? FirstFormRequestBodyParameter { get; set; }
+ // Properties for constructing and managing filters
+ public List ContextArgAccess { get; } = new();
+ public Expression? MethodCall { get; set; }
+ public Type[] ArgumentTypes { get; set; } = Array.Empty();
+ public Expression[]? ArgumentExpressions { get; set; }
+ public Expression[] BoxedArgs { get; set; } = Array.Empty();
+ public bool FilterFactoriesHaveRunWithoutModifyingPerRequestBehavior { get; set; }
+
+ public List Parameters { get; set; } = new();
+}
diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactoryOptions.cs b/src/Http/Http.Extensions/src/RequestDelegateFactoryOptions.cs
index a3f05d9db8a3..a11cba424330 100644
--- a/src/Http/Http.Extensions/src/RequestDelegateFactoryOptions.cs
+++ b/src/Http/Http.Extensions/src/RequestDelegateFactoryOptions.cs
@@ -34,25 +34,18 @@ public sealed class RequestDelegateFactoryOptions
public bool DisableInferBodyFromParameters { get; init; }
///
- /// The list of filters that must run in the pipeline for a given route handler.
- ///
- public IReadOnlyList>? EndpointFilterFactories { get; init; }
-
- ///
- /// The mutable initial endpoint metadata to add as part of the creation of the . In most cases,
- /// this should come from .
+ /// The mutable used to assist in the creation of the .
+ /// This is primarily used to run and populate inferred .
+ /// The must be . After the call to ,
+ /// will be the same as .
///
///
- /// This metadata will be included in before most metadata inferred during creation of the
- /// and before any metadata provided by types in the delegate signature that implement
- /// or . The exception to this general rule is the
+ /// Any metadata already in will be included in before
+ /// most metadata inferred during creation of the and before any metadata provided by types in
+ /// the delegate signature that implement or . The exception to this general rule is the
/// that infers automatically
/// without any custom metadata providers which instead is inserted at the start to give it lower precedence. Custom metadata providers can choose to
/// insert their metadata at the start to give lower precedence, but this is unusual.
///
- public IList? EndpointMetadata { get; init; }
-
- // TODO: Add a RouteEndpointBuilder property and remove the EndpointMetadata property. Then do the same in RouteHandlerContext, EndpointMetadataContext
- // and EndpointParameterMetadataContext. This will allow seeing the entire route pattern if the caller chooses to allow it.
- // We'll probably want to add the RouteEndpointBuilder constructor without a RequestDelegate back and make it public too.
+ public EndpointBuilder? EndpointBuilder { get; init; }
}
diff --git a/src/Http/Http.Extensions/src/RequestDelegateMetadataResult.cs b/src/Http/Http.Extensions/src/RequestDelegateMetadataResult.cs
new file mode 100644
index 000000000000..3ca91ac8bacf
--- /dev/null
+++ b/src/Http/Http.Extensions/src/RequestDelegateMetadataResult.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Http;
+
+///
+/// The metadata inferred by .
+/// will be automatically populated with this metadata if provided.
+/// If this is passed to ,
+/// it will not repeat metadata inference. Any metadata that would be inferred should already be stored in the EndpointBuilder.
+///
+public sealed class RequestDelegateMetadataResult
+{
+ ///
+ /// Gets endpoint metadata inferred from creating the . If a non-null
+ /// RequestDelegateFactoryOptions.EndpointMetadata list was passed in, this will be the same instance.
+ ///
+ public required IReadOnlyList EndpointMetadata { get; init; }
+
+ // This internal cached context avoids redoing unnecessary reflection in Create that was already done in InferMetadata.
+ // InferMetadata currently does more work than it needs to building up expression trees, but the expectation is that InferMetadata will usually be followed by Create.
+ internal RequestDelegateFactoryContext? CachedFactoryContext { get; set; }
+}
diff --git a/src/Http/Http.Extensions/test/EndpointMetadataContextTests.cs b/src/Http/Http.Extensions/test/EndpointMetadataContextTests.cs
deleted file mode 100644
index 3fa0a9a143c6..000000000000
--- a/src/Http/Http.Extensions/test/EndpointMetadataContextTests.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http.Metadata;
-
-namespace Microsoft.AspNetCore.Http.Extensions.Tests;
-
-public class EndpointMetadataContextTests
-{
- [Fact]
- public void EndpointMetadataContext_Ctor_ThrowsArgumentNullException_WhenMethodInfoIsNull()
- {
- Assert.Throws("method", () => new EndpointMetadataContext(null, new List(), null));
- }
-
- [Fact]
- public void EndpointMetadataContext_Ctor_ThrowsArgumentNullException_WhenMetadataIsNull()
- {
- Delegate handler = (int id) => { };
- var method = handler.GetMethodInfo();
-
- Assert.Throws("endpointMetadata", () => new EndpointMetadataContext(method, null, null));
- }
-
- [Fact]
- public void EndpointMetadataContext_Ctor_ThrowsArgumentNullException_WhenApplicationServicesAreNull()
- {
- Delegate handler = (int id) => { };
- var method = handler.GetMethodInfo();
-
- Assert.Throws("applicationServices", () => new EndpointMetadataContext(method, new List(), null));
- }
-
- [Fact]
- public void EndpointParameterMetadataContext_Ctor_ThrowsArgumentNullException_WhenParameterInfoIsNull()
- {
- Assert.Throws("parameter", () => new EndpointParameterMetadataContext(null, new List(), null));
- }
-
- [Fact]
- public void EndpointParameterMetadataContext_Ctor_ThrowsArgumentNullException_WhenMetadataIsNull()
- {
- Delegate handler = (int id) => { };
- var parameter = handler.GetMethodInfo().GetParameters()[0];
-
- Assert.Throws("endpointMetadata", () => new EndpointParameterMetadataContext(parameter, null, null));
- }
-
- [Fact]
- public void EndpointParameterMetadataContext_Ctor_ThrowsArgumentNullException_WhenApplicationServicesAreNull()
- {
- Delegate handler = (int id) => { };
- var parameter = handler.GetMethodInfo().GetParameters()[0];
-
- Assert.Throws("applicationServices", () => new EndpointParameterMetadataContext(parameter, new List(), null));
- }
-}
diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
index 3517fe81b4f8..63f4e1d0c3c0 100644
--- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
+++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
@@ -4,6 +4,7 @@
#nullable enable
using System.Buffers;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO.Pipelines;
using System.Linq.Expressions;
@@ -18,10 +19,12 @@
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Http.Metadata;
+using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Internal;
@@ -5164,14 +5167,14 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
context.Arguments[0] = context.Arguments[0] != null ? $"{((string)context.Arguments[0]!)}Prefix" : "NULL";
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5196,13 +5199,13 @@ public async Task RequestDelegateFactory_InvokesFilters_OnDelegateWithTarget()
// Act
var factoryResult = RequestDelegateFactory.Create((string name) => $"Hello, {name}!", new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5238,13 +5241,13 @@ public async Task RequestDelegateFactory_InvokesFilters_OnMethodInfoWithNullTarg
// Act
var factoryResult = RequestDelegateFactory.Create(methodInfo!, null, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5281,13 +5284,13 @@ public async Task RequestDelegateFactory_InvokesFilters_OnMethodInfoWithProvided
};
var factoryResult = RequestDelegateFactory.Create(methodInfo!, targetFactory, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5318,7 +5321,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>() {
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>() {
(routeHandlerContext, next) => async (context) =>
{
if (context.HttpContext.Response.StatusCode == 400)
@@ -5327,7 +5330,7 @@ string HelloName(string name)
}
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5364,7 +5367,7 @@ string HelloName(string name, int age)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
@@ -5379,7 +5382,7 @@ string HelloName(string name, int age)
}
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5412,7 +5415,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) =>
{
@@ -5428,7 +5431,7 @@ string HelloName(string name)
return "Is not an int.";
};
},
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5439,7 +5442,7 @@ string HelloName(string name)
}
[Fact]
- public async Task RequestDelegateFactory_CanInvokeEndpointFilter_ThatUsesEndpointMetadata()
+ public async Task RequestDelegateFactory_CanInvokeEndpointFilter_ThatReadsEndpointMetadata()
{
// Arrange
string HelloName(IFormFileCollection formFiles)
@@ -5467,26 +5470,32 @@ string HelloName(IFormFileCollection formFiles)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) =>
{
- var acceptsMetadata = routeHandlerContext.EndpointMetadata.OfType();
- var contentType = acceptsMetadata.SingleOrDefault()?.ContentTypes.SingleOrDefault();
+ string? contentType = null;
return async (context) =>
{
+ contentType ??= context.HttpContext.GetEndpoint()?.Metadata.GetMetadata()?.ContentTypes.SingleOrDefault();
+
if (contentType == "multipart/form-data")
{
return "I see you expect a form.";
}
+
return await next(context);
};
},
- }
+ }),
});
- var requestDelegate = factoryResult.RequestDelegate;
- await requestDelegate(httpContext);
+
+ var builder = new RouteEndpointBuilder(factoryResult.RequestDelegate, RoutePatternFactory.Parse("/"), order: 0);
+ ((List)builder.Metadata).AddRange(factoryResult.EndpointMetadata);
+ httpContext.Features.Set(new EndpointFeature { Endpoint = builder.Build() });
+
+ await factoryResult.RequestDelegate(httpContext);
// Assert
var responseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray());
@@ -5518,7 +5527,7 @@ string PrintTodo(Todo todo)
// Act
var factoryResult = RequestDelegateFactory.Create(PrintTodo, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
@@ -5527,7 +5536,7 @@ string PrintTodo(Todo todo)
context.Arguments[0] = originalTodo;
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5559,7 +5568,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
@@ -5570,7 +5579,7 @@ string HelloName(string name)
}
return previousResult;
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5602,7 +5611,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
@@ -5619,7 +5628,7 @@ string HelloName(string name)
context.Arguments[0] = newValue;
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5671,13 +5680,13 @@ public async Task CanInvokeFilter_OnTaskOfTReturningHandler(Delegate @delegate)
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5729,13 +5738,13 @@ public async Task CanInvokeFilter_OnValueTaskOfTReturningHandler(Delegate @deleg
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5794,13 +5803,13 @@ public async Task CanInvokeFilter_OnVoidReturningHandler(Delegate @delegate)
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5828,13 +5837,13 @@ async Task HandlerWithTaskAwait(HttpContext c)
// Act
var factoryResult = RequestDelegateFactory.Create(HandlerWithTaskAwait, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
@@ -5895,13 +5904,13 @@ public async Task CanInvokeFilter_OnHandlerReturningTasksOfStruct(Delegate @dele
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5931,7 +5940,7 @@ string HelloName(int? one, string? two, int? three, string? four, int? five, boo
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
@@ -5939,7 +5948,7 @@ string HelloName(int? one, string? two, int? three, string? four, int? five, boo
Assert.Equal(11, context.Arguments.Count);
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5962,7 +5971,7 @@ string HelloName()
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) => async (context) =>
{
@@ -5970,7 +5979,7 @@ string HelloName()
Assert.Equal(0, context.Arguments.Count);
return await next(context);
}
- }
+ }),
});
var requestDelegate = factoryResult.RequestDelegate;
await requestDelegate(httpContext);
@@ -5996,7 +6005,7 @@ public void Create_DoesNotAddAnythingBefore_ThePassedInEndpointMetadata()
// Arrange
var @delegate = (AddsCustomParameterMetadataBindable param1) => { };
var customMetadata = new CustomEndpointMetadata();
- var options = new RequestDelegateFactoryOptions { EndpointMetadata = new List { customMetadata } };
+ var options = new RequestDelegateFactoryOptions { EndpointBuilder = CreateEndpointBuilder(new List { customMetadata }) };
// Act
var result = RequestDelegateFactory.Create(@delegate, options);
@@ -6097,10 +6106,10 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromReturnTypesImplementin
var @delegate = () => new CountsDefaultEndpointMetadataResult();
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6109,7 +6118,7 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromReturnTypesImplementin
// 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 DefaultMetadataCountMetadata { Count: 1 });
+ Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 });
}
[Fact]
@@ -6119,10 +6128,10 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromTaskWrappedReturnTypes
var @delegate = () => Task.FromResult(new CountsDefaultEndpointMetadataResult());
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6131,7 +6140,7 @@ 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 DefaultMetadataCountMetadata { Count: 1 });
+ Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 });
}
[Fact]
@@ -6141,10 +6150,10 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromValueTaskWrappedReturn
var @delegate = () => ValueTask.FromResult(new CountsDefaultEndpointMetadataResult());
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6153,7 +6162,7 @@ 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 DefaultMetadataCountMetadata { Count: 1 });
+ Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 });
}
[Fact]
@@ -6163,10 +6172,10 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromParameterTypesImplemen
var @delegate = (AddsCustomParameterMetadata param1) => "Hello";
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6184,10 +6193,10 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromParameterTypesImplemen
var @delegate = (AddsCustomParameterMetadata param1) => "Hello";
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6205,10 +6214,10 @@ public void Create_CombinesPropertiesAsParameterMetadata_AndTopLevelParameter()
var @delegate = ([AsParameters] AddsCustomParameterMetadata param1) => new CountsDefaultEndpointMetadataResult();
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6228,10 +6237,10 @@ public void Create_CombinesAllMetadata_InCorrectOrder()
var @delegate = [Attribute1, Attribute2] (AddsCustomParameterMetadata param1) => new CountsDefaultEndpointMetadataResult();
var options = new RequestDelegateFactoryOptions
{
- EndpointMetadata = new List
+ EndpointBuilder = CreateEndpointBuilder(new List
{
new CustomEndpointMetadata { Source = MetadataSource.Caller }
- }
+ }),
};
// Act
@@ -6239,16 +6248,106 @@ public void Create_CombinesAllMetadata_InCorrectOrder()
// Assert
Assert.Collection(result.EndpointMetadata,
+ // Initial metadata from RequestDelegateFactoryOptions.EndpointBuilder. If the caller want to override inferred metadata,
+ // They need to call InferMetadata first, then add the overriding metadata, and then call Create with InferMetadata's result.
+ // This is demonstrated in the following tests.
+ 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)),
- // Initial metadata from RequestDelegateFactoryOptions.InitialEndpointMetadata
- m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Caller }),
// Metadata provided by parameters implementing IEndpointParameterMetadataProvider
m => Assert.True(m is ParameterNameMetadata { Name: "param1" }),
// 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 DefaultMetadataCountMetadata { Count: 4 }));
+ m => Assert.True(m is MetadataCountMetadata { Count: 4 }));
+ }
+
+ [Fact]
+ public void Create_FlowsRoutePattern_ToMetadataProvider()
+ {
+ // Arrange
+ var @delegate = (AddsRoutePatternMetadata param1) => { };
+ var builder = new RouteEndpointBuilder(requestDelegate: null, RoutePatternFactory.Parse("/test/pattern"), order: 0);
+ var options = new RequestDelegateFactoryOptions
+ {
+ EndpointBuilder = builder,
+ };
+
+ // Act
+ var result = RequestDelegateFactory.Create(@delegate, options);
+
+ // Assert
+ Assert.Contains(result.EndpointMetadata, m => m is RoutePatternMetadata { RoutePattern: "/test/pattern" });
+ }
+
+ [Fact]
+ public void Create_DoesNotInferMetadata_GivenManuallyConstructedMetadataResult()
+ {
+ var invokeCount = 0;
+
+ // Arrange
+ var @delegate = [Attribute1, Attribute2] (AddsCustomParameterMetadata param1) =>
+ {
+ invokeCount++;
+ return new CountsDefaultEndpointMetadataResult();
+ };
+
+ var options = new RequestDelegateFactoryOptions
+ {
+ EndpointBuilder = CreateEndpointBuilder(),
+ };
+ var metadataResult = new RequestDelegateMetadataResult { EndpointMetadata = new List() };
+ var httpContext = CreateHttpContext();
+
+ // An empty object should deserialize to AddsCustomParameterMetadata since it has no required properties.
+ var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new object());
+ var stream = new MemoryStream(requestBodyBytes);
+ httpContext.Request.Body = stream;
+
+ httpContext.Request.Headers["Content-Type"] = "application/json";
+ httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture);
+ httpContext.Features.Set(new RequestBodyDetectionFeature(true));
+
+ // Act
+ var result = RequestDelegateFactory.Create(@delegate, options, metadataResult);
+
+ // Assert
+ Assert.Empty(result.EndpointMetadata);
+ 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,
+ // and most of the other unit tests don't pass in a metadataResult without a cached factory context.
+ Assert.True(result.RequestDelegate(httpContext).IsCompletedSuccessfully);
+ Assert.Equal(1, invokeCount);
+ }
+
+ [Fact]
+ public void InferMetadata_ThenCreate_CombinesAllMetadata_InCorrectOrder()
+ {
+ // Arrange
+ var @delegate = [Attribute1, Attribute2] (AddsCustomParameterMetadata param1) => new CountsDefaultEndpointMetadataResult();
+ var options = new RequestDelegateFactoryOptions
+ {
+ EndpointBuilder = CreateEndpointBuilder(),
+ };
+
+ // Act
+ var metadataResult = RequestDelegateFactory.InferMetadata(@delegate.Method, options);
+ options.EndpointBuilder.Metadata.Add(new CustomEndpointMetadata { Source = MetadataSource.Caller });
+ var result = RequestDelegateFactory.Create(@delegate, options, metadataResult);
+
+ // Assert
+ Assert.Collection(result.EndpointMetadata,
+ // Inferred AcceptsMetadata from RDF for complex type
+ m => Assert.True(m is AcceptsMetadata am && am.RequestType == typeof(AddsCustomParameterMetadata)),
+ // Metadata provided by parameters implementing IEndpointParameterMetadataProvider
+ m => Assert.True(m is ParameterNameMetadata { Name: "param1" }),
+ // 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: 3 }),
+ // Entry-specific metadata added after a call to InferMetadata
+ m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Caller }));
}
[Fact]
@@ -6351,31 +6450,44 @@ public void Create_SetsApplicationServices_OnEndpointParameterMetadataContext()
public void Create_ReturnsSameRequestDelegatePassedIn_IfNotModifiedByFilters()
{
RequestDelegate initialRequestDelegate = static (context) => Task.CompletedTask;
- var filter1Tag = new TagsAttribute("filter1");
- var filter2Tag = new TagsAttribute("filter2");
+ var invokeCount = 0;
RequestDelegateFactoryOptions options = new()
{
- EndpointFilterFactories = new List>()
+ EndpointBuilder = CreateEndpointBuilderFromFilterFactories(new List>()
{
(routeHandlerContext, next) =>
{
- routeHandlerContext.EndpointMetadata.Add(filter1Tag);
+ invokeCount++;
return next;
},
(routeHandlerContext, next) =>
{
- routeHandlerContext.EndpointMetadata.Add(filter2Tag);
+ invokeCount++;
return next;
},
- }
+ }),
};
var result = RequestDelegateFactory.Create(initialRequestDelegate, options);
Assert.Same(initialRequestDelegate, result.RequestDelegate);
- Assert.Collection(result.EndpointMetadata,
- m => Assert.Same(filter2Tag, m),
- m => Assert.Same(filter1Tag, m));
+ Assert.Equal(2, invokeCount);
+ }
+
+ [Fact]
+ public void Create_Populates_EndpointBuilderWithRequestDelegateAndMetadata()
+ {
+ RequestDelegate requestDelegate = static context => Task.CompletedTask;
+
+ RequestDelegateFactoryOptions options = new()
+ {
+ EndpointBuilder = new RouteEndpointBuilder(null, RoutePatternFactory.Parse("/"), order: 0),
+ };
+
+ var result = RequestDelegateFactory.Create(requestDelegate, options);
+
+ Assert.Same(options.EndpointBuilder.RequestDelegate, result.RequestDelegate);
+ Assert.Same(options.EndpointBuilder.Metadata, result.EndpointMetadata);
}
private DefaultHttpContext CreateHttpContext()
@@ -6394,15 +6506,32 @@ private DefaultHttpContext CreateHttpContext()
};
}
+ private EndpointBuilder CreateEndpointBuilder(IEnumerable? metadata = null)
+ {
+ var builder = new RouteEndpointBuilder(null, RoutePatternFactory.Parse("/"), 0);
+ if (metadata is not null)
+ {
+ ((List)builder.Metadata).AddRange(metadata);
+ }
+ return builder;
+ }
+
+ private EndpointBuilder CreateEndpointBuilderFromFilterFactories(IEnumerable> filterFactories)
+ {
+ var builder = new RouteEndpointBuilder(null, RoutePatternFactory.Parse("/"), 0);
+ ((List>)builder.FilterFactories).AddRange(filterFactories);
+ return builder;
+ }
+
private record MetadataService;
private class AccessesServicesMetadataResult : IResult, IEndpointMetadataProvider
{
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- if (context.ApplicationServices?.GetRequiredService() is { } metadataService)
+ if (builder.ApplicationServices.GetRequiredService() is { } metadataService)
{
- context.EndpointMetadata.Add(metadataService);
+ builder.Metadata.Add(metadataService);
}
}
@@ -6414,11 +6543,11 @@ private class AccessesServicesMetadataBinder : IEndpointMetadataProvider
public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) =>
new(new AccessesServicesMetadataBinder());
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- if (context.ApplicationServices?.GetRequiredService() is { } metadataService)
+ if (builder.ApplicationServices.GetRequiredService() is { } metadataService)
{
- context.EndpointMetadata.Add(metadataService);
+ builder.Metadata.Add(metadataService);
}
}
}
@@ -6433,9 +6562,9 @@ private class Attribute2 : Attribute
private class AddsCustomEndpointMetadataResult : IEndpointMetadataProvider, IResult
{
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- context.EndpointMetadata?.Add(new CustomEndpointMetadata { Source = MetadataSource.ReturnType });
+ builder.Metadata.Add(new CustomEndpointMetadata { Source = MetadataSource.ReturnType });
}
public Task ExecuteAsync(HttpContext httpContext) => throw new NotImplementedException();
@@ -6443,7 +6572,7 @@ public static void PopulateMetadata(EndpointMetadataContext context)
private class AddsNoEndpointMetadataResult : IEndpointMetadataProvider, IResult
{
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
}
@@ -6453,27 +6582,27 @@ public static void PopulateMetadata(EndpointMetadataContext context)
private class CountsDefaultEndpointMetadataResult : IEndpointMetadataProvider, IResult
{
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- var defaultMetadataCount = context.EndpointMetadata?.Count;
- context.EndpointMetadata?.Add(new DefaultMetadataCountMetadata { Count = defaultMetadataCount ?? 0 });
+ var currentMetadataCount = builder.Metadata.Count;
+ builder.Metadata.Add(new MetadataCountMetadata { Count = currentMetadataCount });
}
- public Task ExecuteAsync(HttpContext httpContext) => throw new NotImplementedException();
+ public Task ExecuteAsync(HttpContext httpContext) => Task.CompletedTask;
}
private class RemovesAcceptsParameterMetadata : IEndpointParameterMetadataProvider
{
- public static void PopulateMetadata(EndpointParameterMetadataContext parameterContext)
+ public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
- if (parameterContext.EndpointMetadata is not null)
+ if (builder.Metadata is not null)
{
- for (int i = parameterContext.EndpointMetadata.Count - 1; i >= 0; i--)
+ for (int i = builder.Metadata.Count - 1; i >= 0; i--)
{
- var metadata = parameterContext.EndpointMetadata[i];
+ var metadata = builder.Metadata[i];
if (metadata is IAcceptsMetadata)
{
- parameterContext.EndpointMetadata.RemoveAt(i);
+ builder.Metadata.RemoveAt(i);
}
}
}
@@ -6482,16 +6611,16 @@ public static void PopulateMetadata(EndpointParameterMetadataContext parameterCo
private class RemovesAcceptsMetadata : IEndpointMetadataProvider
{
- public static void PopulateMetadata(EndpointMetadataContext parameterContext)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- if (parameterContext.EndpointMetadata is not null)
+ if (builder.Metadata is not null)
{
- for (int i = parameterContext.EndpointMetadata.Count - 1; i >= 0; i--)
+ for (int i = builder.Metadata.Count - 1; i >= 0; i--)
{
- var metadata = parameterContext.EndpointMetadata[i];
+ var metadata = builder.Metadata[i];
if (metadata is IAcceptsMetadata)
{
- parameterContext.EndpointMetadata.RemoveAt(i);
+ builder.Metadata.RemoveAt(i);
}
}
}
@@ -6500,16 +6629,16 @@ public static void PopulateMetadata(EndpointMetadataContext parameterContext)
private class RemovesAcceptsMetadataResult : IEndpointMetadataProvider, IResult
{
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- if (context.EndpointMetadata is not null)
+ if (builder.Metadata is not null)
{
- for (int i = context.EndpointMetadata.Count - 1; i >= 0; i--)
+ for (int i = builder.Metadata.Count - 1; i >= 0; i--)
{
- var metadata = context.EndpointMetadata[i];
+ var metadata = builder.Metadata[i];
if (metadata is IAcceptsMetadata)
{
- context.EndpointMetadata.RemoveAt(i);
+ builder.Metadata.RemoveAt(i);
}
}
}
@@ -6520,48 +6649,79 @@ public static void PopulateMetadata(EndpointMetadataContext context)
private class AddsCustomParameterMetadataAsProperty : IEndpointParameterMetadataProvider, IEndpointMetadataProvider
{
- public static void PopulateMetadata(EndpointParameterMetadataContext parameterContext)
+ public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
- parameterContext.EndpointMetadata?.Add(new ParameterNameMetadata { Name = parameterContext.Parameter?.Name });
+ builder.Metadata.Add(new ParameterNameMetadata { Name = parameter.Name });
}
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- context.EndpointMetadata?.Add(new CustomEndpointMetadata { Source = MetadataSource.Property });
+ builder.Metadata.Add(new CustomEndpointMetadata { Source = MetadataSource.Property });
}
}
- private class AddsCustomParameterMetadata : IEndpointParameterMetadataProvider, IEndpointMetadataProvider
+ // TODO: Binding breaks if we explicitly implement IParsable. :(
+ // We could special-case IParsable because we have a reference to it. The check for `!method.IsAbstract` in GetStaticMethodFromHierarchy
+ // stops us from finding it now. But even if we did find it, we haven't implemented the correct code gen to call it for unreferenced interfaces.
+ // We might have to use Type.GetInterfaceMap. See previous discussion: https://github.com/dotnet/aspnetcore/pull/40926#discussion_r837781209
+ //
+ // System.InvalidOperationException : TryParse method found on AddsCustomParameterMetadata with incorrect format. Must be a static method with format
+ // bool TryParse(string, IFormatProvider, out AddsCustomParameterMetadata)
+ // bool TryParse(string, out AddsCustomParameterMetadata)
+ // but found
+ // static Boolean TryParse(System.String, System.IFormatProvider, AddsCustomParameterMetadata ByRef)
+ private class AddsCustomParameterMetadata : IEndpointParameterMetadataProvider, IEndpointMetadataProvider//, IParsable
{
public AddsCustomParameterMetadataAsProperty? Data { get; set; }
- public static void PopulateMetadata(EndpointParameterMetadataContext parameterContext)
+ public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
- parameterContext.EndpointMetadata?.Add(new ParameterNameMetadata { Name = parameterContext.Parameter?.Name });
+ builder.Metadata.Add(new ParameterNameMetadata { Name = parameter.Name });
}
- public static void PopulateMetadata(EndpointMetadataContext context)
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- context.EndpointMetadata?.Add(new CustomEndpointMetadata { Source = MetadataSource.Parameter });
+ builder.Metadata.Add(new CustomEndpointMetadata { Source = MetadataSource.Parameter });
}
+
+ //static bool IParsable.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out AddsCustomParameterMetadata result)
+ //{
+ // result = new();
+ // return true;
+ //}
+
+ //static AddsCustomParameterMetadata IParsable.Parse(string s, IFormatProvider? provider) => throw new NotSupportedException();
}
private class AddsCustomParameterMetadataBindable : IEndpointParameterMetadataProvider, IEndpointMetadataProvider
{
public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) => default;
- public static void PopulateMetadata(EndpointParameterMetadataContext parameterContext)
+ public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
+ {
+ builder.Metadata.Add(new ParameterNameMetadata { Name = parameter.Name });
+ }
+
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- parameterContext.EndpointMetadata?.Add(new ParameterNameMetadata { Name = parameterContext.Parameter?.Name });
+ builder.Metadata.Add(new CustomEndpointMetadata { Source = MetadataSource.Parameter });
}
+ }
- public static void PopulateMetadata(EndpointMetadataContext context)
+ private class AddsRoutePatternMetadata : IEndpointMetadataProvider
+ {
+ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- context.EndpointMetadata?.Add(new CustomEndpointMetadata { Source = MetadataSource.Parameter });
+ if (builder is not RouteEndpointBuilder reb)
+ {
+ return;
+ }
+
+ builder.Metadata.Add(new RoutePatternMetadata { RoutePattern = reb.RoutePattern?.RawText ?? string.Empty });
}
}
- private class DefaultMetadataCountMetadata
+ private class MetadataCountMetadata
{
public int Count { get; init; }
}
@@ -6578,6 +6738,11 @@ private class CustomEndpointMetadata
public MetadataSource Source { get; init; }
}
+ private class RoutePatternMetadata
+ {
+ public string RoutePattern { get; init; } = String.Empty;
+ }
+
private enum MetadataSource
{
Caller,
@@ -6929,6 +7094,11 @@ public TlsConnectionFeature(X509Certificate2 clientCertificate)
throw new NotImplementedException();
}
}
+
+ private class EndpointFeature : IEndpointFeature
+ {
+ public Endpoint? Endpoint { get; set; }
+ }
}
internal static class TestExtensionResults
diff --git a/src/Http/Http.Results/src/Accepted.cs b/src/Http/Http.Results/src/Accepted.cs
index 42bd0b4ed9d8..0a23b209c2b6 100644
--- a/src/Http/Http.Results/src/Accepted.cs
+++ b/src/Http/Http.Results/src/Accepted.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -74,10 +76,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted));
}
}
diff --git a/src/Http/Http.Results/src/AcceptedAtRoute.cs b/src/Http/Http.Results/src/AcceptedAtRoute.cs
index 5698f463744f..55489db2d29c 100644
--- a/src/Http/Http.Results/src/AcceptedAtRoute.cs
+++ b/src/Http/Http.Results/src/AcceptedAtRoute.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@@ -86,10 +88,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted));
}
}
diff --git a/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs b/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs
index af9a471cf2c0..c270926e5223 100644
--- a/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs
+++ b/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@@ -99,10 +101,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status202Accepted, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status202Accepted, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/AcceptedOfT.cs b/src/Http/Http.Results/src/AcceptedOfT.cs
index 1911881fc4cc..cc3051108d36 100644
--- a/src/Http/Http.Results/src/AcceptedOfT.cs
+++ b/src/Http/Http.Results/src/AcceptedOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -96,10 +98,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status202Accepted, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status202Accepted, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/BadRequest.cs b/src/Http/Http.Results/src/BadRequest.cs
index d2bb333542fc..e27c56889ae5 100644
--- a/src/Http/Http.Results/src/BadRequest.cs
+++ b/src/Http/Http.Results/src/BadRequest.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -44,10 +46,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status400BadRequest));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status400BadRequest));
}
}
diff --git a/src/Http/Http.Results/src/BadRequestOfT.cs b/src/Http/Http.Results/src/BadRequestOfT.cs
index 9056667b79c1..c9d48ed8be4f 100644
--- a/src/Http/Http.Results/src/BadRequestOfT.cs
+++ b/src/Http/Http.Results/src/BadRequestOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -58,10 +60,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status400BadRequest, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status400BadRequest, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/Conflict.cs b/src/Http/Http.Results/src/Conflict.cs
index 086ecd1f9ba4..5504c2cab069 100644
--- a/src/Http/Http.Results/src/Conflict.cs
+++ b/src/Http/Http.Results/src/Conflict.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -44,10 +46,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status409Conflict));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status409Conflict));
}
}
diff --git a/src/Http/Http.Results/src/ConflictOfT.cs b/src/Http/Http.Results/src/ConflictOfT.cs
index baf16f6fc85a..f78bf8b8b76c 100644
--- a/src/Http/Http.Results/src/ConflictOfT.cs
+++ b/src/Http/Http.Results/src/ConflictOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -58,10 +60,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status409Conflict, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status409Conflict, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/Created.cs b/src/Http/Http.Results/src/Created.cs
index 02091f69db8c..6c4be159c3c0 100644
--- a/src/Http/Http.Results/src/Created.cs
+++ b/src/Http/Http.Results/src/Created.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -76,10 +78,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status201Created));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status201Created));
}
}
diff --git a/src/Http/Http.Results/src/CreatedAtRoute.cs b/src/Http/Http.Results/src/CreatedAtRoute.cs
index a3e8d34cb4af..213576e24e52 100644
--- a/src/Http/Http.Results/src/CreatedAtRoute.cs
+++ b/src/Http/Http.Results/src/CreatedAtRoute.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@@ -86,10 +88,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status201Created));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status201Created));
}
}
diff --git a/src/Http/Http.Results/src/CreatedAtRouteOfT.cs b/src/Http/Http.Results/src/CreatedAtRouteOfT.cs
index 1a64b3415a20..6d543d9b98f5 100644
--- a/src/Http/Http.Results/src/CreatedAtRouteOfT.cs
+++ b/src/Http/Http.Results/src/CreatedAtRouteOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@@ -102,10 +104,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status201Created, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status201Created, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/CreatedOfT.cs b/src/Http/Http.Results/src/CreatedOfT.cs
index b6104f30c0c6..0e8cfea4a217 100644
--- a/src/Http/Http.Results/src/CreatedOfT.cs
+++ b/src/Http/Http.Results/src/CreatedOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -94,10 +96,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status201Created, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status201Created, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/NoContent.cs b/src/Http/Http.Results/src/NoContent.cs
index f52e0ad4bd94..75c8cfa2c1c7 100644
--- a/src/Http/Http.Results/src/NoContent.cs
+++ b/src/Http/Http.Results/src/NoContent.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -44,10 +46,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status204NoContent));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status204NoContent));
}
}
diff --git a/src/Http/Http.Results/src/NotFound.cs b/src/Http/Http.Results/src/NotFound.cs
index 069c2d7ad62c..67df2f05af2c 100644
--- a/src/Http/Http.Results/src/NotFound.cs
+++ b/src/Http/Http.Results/src/NotFound.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -43,10 +45,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status404NotFound));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status404NotFound));
}
}
diff --git a/src/Http/Http.Results/src/NotFoundOfT.cs b/src/Http/Http.Results/src/NotFoundOfT.cs
index 18a39d02f42a..f358691e1ecd 100644
--- a/src/Http/Http.Results/src/NotFoundOfT.cs
+++ b/src/Http/Http.Results/src/NotFoundOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -57,10 +59,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status404NotFound, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status404NotFound, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/Ok.cs b/src/Http/Http.Results/src/Ok.cs
index ab13c4565974..110058a2591c 100644
--- a/src/Http/Http.Results/src/Ok.cs
+++ b/src/Http/Http.Results/src/Ok.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -43,10 +45,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status200OK));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status200OK));
}
}
diff --git a/src/Http/Http.Results/src/OkOfT.cs b/src/Http/Http.Results/src/OkOfT.cs
index 532837bea11e..d5a9646f70f4 100644
--- a/src/Http/Http.Results/src/OkOfT.cs
+++ b/src/Http/Http.Results/src/OkOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -57,10 +59,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status200OK, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status200OK, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/ResultsOfT.Generated.cs b/src/Http/Http.Results/src/ResultsOfT.Generated.cs
index c468a4e7541e..6034e656b573 100644
--- a/src/Http/Http.Results/src/ResultsOfT.Generated.cs
+++ b/src/Http/Http.Results/src/ResultsOfT.Generated.cs
@@ -3,6 +3,8 @@
// This file is generated by a tool. See: src/Http/Http.Results/tools/ResultsOfTGenerator
+using System.Reflection;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
namespace Microsoft.AspNetCore.Http.HttpResults;
@@ -59,12 +61,13 @@ public Task ExecuteAsync(HttpContext httpContext)
public static implicit operator Results(TResult2 result) => new(result);
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
}
}
@@ -128,13 +131,14 @@ public Task ExecuteAsync(HttpContext httpContext)
public static implicit operator Results(TResult3 result) => new(result);
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
}
}
@@ -206,14 +210,15 @@ public Task ExecuteAsync(HttpContext httpContext)
public static implicit operator Results(TResult4 result) => new(result);
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
}
}
@@ -293,15 +298,16 @@ public Task ExecuteAsync(HttpContext httpContext)
public static implicit operator Results(TResult5 result) => new(result);
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
-
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
+
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
}
}
@@ -389,15 +395,16 @@ public Task ExecuteAsync(HttpContext httpContext)
public static implicit operator Results(TResult6 result) => new(result);
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
-
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
- ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
+
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
+ ResultsOfTHelper.PopulateMetadataIfTargetIsIEndpointMetadataProvider(method, builder);
}
}
diff --git a/src/Http/Http.Results/src/ResultsOfTHelper.cs b/src/Http/Http.Results/src/ResultsOfTHelper.cs
index 7f6361167158..a68f55c6dfee 100644
--- a/src/Http/Http.Results/src/ResultsOfTHelper.cs
+++ b/src/Http/Http.Results/src/ResultsOfTHelper.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Reflection;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
namespace Microsoft.AspNetCore.Http;
@@ -10,16 +11,16 @@ internal static class ResultsOfTHelper
{
private static readonly MethodInfo PopulateMetadataMethod = typeof(ResultsOfTHelper).GetMethod(nameof(PopulateMetadata), BindingFlags.Static | BindingFlags.NonPublic)!;
- public static void PopulateMetadataIfTargetIsIEndpointMetadataProvider(EndpointMetadataContext context)
+ public static void PopulateMetadataIfTargetIsIEndpointMetadataProvider(MethodInfo method, EndpointBuilder builder)
{
if (typeof(IEndpointMetadataProvider).IsAssignableFrom(typeof(TTarget)))
{
- PopulateMetadataMethod.MakeGenericMethod(typeof(TTarget)).Invoke(null, new object[] { context });
+ PopulateMetadataMethod.MakeGenericMethod(typeof(TTarget)).Invoke(null, new object[] { method, builder });
}
}
- private static void PopulateMetadata(EndpointMetadataContext context) where TTarget : IEndpointMetadataProvider
+ private static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) where TTarget : IEndpointMetadataProvider
{
- TTarget.PopulateMetadata(context);
+ TTarget.PopulateMetadata(method, builder);
}
}
diff --git a/src/Http/Http.Results/src/UnprocessableEntity.cs b/src/Http/Http.Results/src/UnprocessableEntity.cs
index 6a56964d17ba..cc38731f1165 100644
--- a/src/Http/Http.Results/src/UnprocessableEntity.cs
+++ b/src/Http/Http.Results/src/UnprocessableEntity.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -44,10 +46,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status422UnprocessableEntity));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status422UnprocessableEntity));
}
}
diff --git a/src/Http/Http.Results/src/UnprocessableEntityOfT.cs b/src/Http/Http.Results/src/UnprocessableEntityOfT.cs
index b1abbf6faece..d4eca8216826 100644
--- a/src/Http/Http.Results/src/UnprocessableEntityOfT.cs
+++ b/src/Http/Http.Results/src/UnprocessableEntityOfT.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -58,10 +60,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status422UnprocessableEntity, "application/json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(TValue), StatusCodes.Status422UnprocessableEntity, "application/json"));
}
}
diff --git a/src/Http/Http.Results/src/ValidationProblem.cs b/src/Http/Http.Results/src/ValidationProblem.cs
index 36631d141e8b..84ead95982d5 100644
--- a/src/Http/Http.Results/src/ValidationProblem.cs
+++ b/src/Http/Http.Results/src/ValidationProblem.cs
@@ -1,6 +1,8 @@
// 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;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -65,10 +67,11 @@ public Task ExecuteAsync(HttpContext httpContext)
}
///
- static void IEndpointMetadataProvider.PopulateMetadata(EndpointMetadataContext context)
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
{
- ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
- context.EndpointMetadata.Add(new ProducesResponseTypeMetadata(typeof(HttpValidationProblemDetails), StatusCodes.Status400BadRequest, "application/problem+json"));
+ builder.Metadata.Add(new ProducesResponseTypeMetadata(typeof(HttpValidationProblemDetails), StatusCodes.Status400BadRequest, "application/problem+json"));
}
}
diff --git a/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs b/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs
index 40add2624bca..a4b085a27f9f 100644
--- a/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs
+++ b/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs
@@ -3,8 +3,10 @@
using System.Reflection;
using System.Text;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
@@ -121,14 +123,13 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
{
// Arrange
AcceptedAtRoute MyApi() { throw new NotImplementedException(); }
- var metadata = new List();
- var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);
+ var builder = new RouteEndpointBuilder(requestDelegate: null, RoutePatternFactory.Parse("/"), order: 0);
// Act
- PopulateMetadata>(context);
+ PopulateMetadata>(((Delegate)MyApi).GetMethodInfo(), builder);
// Assert
- var producesResponseTypeMetadata = context.EndpointMetadata.OfType().Last();
+ var producesResponseTypeMetadata = builder.Metadata.OfType().Last();
Assert.Equal(StatusCodes.Status202Accepted, producesResponseTypeMetadata.StatusCode);
Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type);
Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json");
@@ -146,10 +147,11 @@ public void ExecuteAsync_ThrowsArgumentNullException_WhenHttpContextIsNull()
}
[Fact]
- public void PopulateMetadata_ThrowsArgumentNullException_WhenContextIsNull()
+ public void PopulateMetadata_ThrowsArgumentNullException_WhenMethodOrBuilderAreNull()
{
// Act & Assert
- Assert.Throws("context", () => PopulateMetadata>(null));
+ Assert.Throws("method", () => PopulateMetadata>(null, new RouteEndpointBuilder(requestDelegate: null, RoutePatternFactory.Parse("/"), order: 0)));
+ Assert.Throws("builder", () => PopulateMetadata>(((Delegate)PopulateMetadata_ThrowsArgumentNullException_WhenMethodOrBuilderAreNull).GetMethodInfo(), null));
}
[Fact]
@@ -201,8 +203,8 @@ public void AcceptedAtRouteResult_Implements_IValueHttpResultOfT_Correctly()
Assert.Equal(value, result.Value);
}
- private static void PopulateMetadata(EndpointMetadataContext context)
- where TResult : IEndpointMetadataProvider => TResult.PopulateMetadata(context);
+ private static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
+ where TResult : IEndpointMetadataProvider => TResult.PopulateMetadata(method, builder);
private record Todo(int Id, string Title);
diff --git a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs
index 8f00d120d7f4..cf11ba325d86 100644
--- a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs
+++ b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs
@@ -3,8 +3,10 @@
using System.Reflection;
using System.Text;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
@@ -75,13 +77,13 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
AcceptedAtRoute MyApi() { throw new NotImplementedException(); }
var metadata = new List();
- var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);
+ var builder = new RouteEndpointBuilder(requestDelegate: null, RoutePatternFactory.Parse("/"), order: 0);
// Act
- PopulateMetadata(context);
+ PopulateMetadata(((Delegate)MyApi).GetMethodInfo(), builder);
// Assert
- var producesResponseTypeMetadata = context.EndpointMetadata.OfType