diff --git a/src/Http/Http.Abstractions/src/AsParametersAttribute.cs b/src/Http/Http.Abstractions/src/AsParametersAttribute.cs
new file mode 100644
index 000000000000..5ed7ac1b1aba
--- /dev/null
+++ b/src/Http/Http.Abstractions/src/AsParametersAttribute.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Http;
+
+using System;
+
+///
+/// Specifies that a route handler delegate's parameter represents a structured parameter list.
+///
+[AttributeUsage(
+ AttributeTargets.Parameter,
+ Inherited = false,
+ AllowMultiple = false)]
+public sealed class AsParametersAttribute : Attribute
+{
+}
diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
index 54c1f4be51fc..ae9027335d66 100644
--- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
+++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
@@ -3,6 +3,8 @@
*REMOVED*Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object?
Microsoft.AspNetCore.Builder.EndpointBuilder.ServiceProvider.get -> System.IServiceProvider?
Microsoft.AspNetCore.Builder.EndpointBuilder.ServiceProvider.set -> void
+Microsoft.AspNetCore.Http.AsParametersAttribute
+Microsoft.AspNetCore.Http.AsParametersAttribute.AsParametersAttribute() -> void
Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext
Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.DefaultRouteHandlerInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! arguments) -> void
Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object!
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 718f747582be..ba4e362f4db3 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.
@@ -13,6 +13,7 @@
+
diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs
index baa1bd46f166..dfe6c5bef609 100644
--- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs
+++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs
@@ -9,6 +9,7 @@
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;
@@ -222,7 +223,10 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions
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);
+ AddTypeProvidedMetadata(methodInfo,
+ factoryContext.Metadata,
+ factoryContext.ServiceProvider,
+ CollectionsMarshal.AsSpan(factoryContext.Parameters));
// Add method attributes as metadata *after* any inferred metadata so that the attributes hava a higher specificity
AddMethodAttributesAsMetadata(methodInfo, factoryContext.Metadata);
@@ -424,12 +428,11 @@ private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext
return fallbackConstruction;
}
- private static void AddTypeProvidedMetadata(MethodInfo methodInfo, List