Skip to content

Commit d2ab01b

Browse files
authored
Detect services based on service provider (#32737)
* Detect services based on service provider - Use IServiceProviderIsService to detect if a parameter is a service. - As a final fallback, try to detect services from the DI container before falling back to body behavior.
1 parent d7d5f41 commit d2ab01b

File tree

5 files changed

+140
-62
lines changed

5 files changed

+140
-62
lines changed

src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ static Microsoft.AspNetCore.Http.HeaderDictionaryTypeExtensions.AppendList<T>(th
169169
static Microsoft.AspNetCore.Http.HeaderDictionaryTypeExtensions.GetTypedHeaders(this Microsoft.AspNetCore.Http.HttpRequest! request) -> Microsoft.AspNetCore.Http.Headers.RequestHeaders!
170170
static Microsoft.AspNetCore.Http.HeaderDictionaryTypeExtensions.GetTypedHeaders(this Microsoft.AspNetCore.Http.HttpResponse! response) -> Microsoft.AspNetCore.Http.Headers.ResponseHeaders!
171171
static Microsoft.AspNetCore.Http.HttpContextServerVariableExtensions.GetServerVariable(this Microsoft.AspNetCore.Http.HttpContext! context, string! variableName) -> string?
172-
static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Delegate! action) -> Microsoft.AspNetCore.Http.RequestDelegate!
173-
static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo) -> Microsoft.AspNetCore.Http.RequestDelegate!
174-
static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.Func<Microsoft.AspNetCore.Http.HttpContext!, object!>! targetFactory) -> Microsoft.AspNetCore.Http.RequestDelegate!
172+
static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Delegate! action, System.IServiceProvider? serviceProvider) -> Microsoft.AspNetCore.Http.RequestDelegate!
173+
static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.IServiceProvider? serviceProvider) -> Microsoft.AspNetCore.Http.RequestDelegate!
174+
static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.IServiceProvider? serviceProvider, System.Func<Microsoft.AspNetCore.Http.HttpContext!, object!>! targetFactory) -> Microsoft.AspNetCore.Http.RequestDelegate!
175175
static Microsoft.AspNetCore.Http.ResponseExtensions.Clear(this Microsoft.AspNetCore.Http.HttpResponse! response) -> void
176176
static Microsoft.AspNetCore.Http.ResponseExtensions.Redirect(this Microsoft.AspNetCore.Http.HttpResponse! response, string! location, bool permanent, bool preserveMethod) -> void
177177
static Microsoft.AspNetCore.Http.SendFileResponseExtensions.SendFileAsync(this Microsoft.AspNetCore.Http.HttpResponse! response, Microsoft.Extensions.FileProviders.IFileInfo! file, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!

src/Http/Http.Extensions/src/RequestDelegateFactory.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ public static class RequestDelegateFactory
6262
/// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="action"/>.
6363
/// </summary>
6464
/// <param name="action">A request handler with any number of custom parameters that often produces a response with its return value.</param>
65+
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> instance used to detect which parameters are services.</param>
6566
/// <returns>The <see cref="RequestDelegate"/>.</returns>
66-
public static RequestDelegate Create(Delegate action)
67+
public static RequestDelegate Create(Delegate action, IServiceProvider? serviceProvider)
6768
{
6869
if (action is null)
6970
{
@@ -76,7 +77,7 @@ public static RequestDelegate Create(Delegate action)
7677
null => null,
7778
};
7879

79-
var targetableRequestDelegate = CreateTargetableRequestDelegate(action.Method, targetExpression);
80+
var targetableRequestDelegate = CreateTargetableRequestDelegate(action.Method, serviceProvider, targetExpression);
8081

8182
return httpContext =>
8283
{
@@ -88,15 +89,16 @@ public static RequestDelegate Create(Delegate action)
8889
/// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="methodInfo"/>.
8990
/// </summary>
9091
/// <param name="methodInfo">A static request handler with any number of custom parameters that often produces a response with its return value.</param>
92+
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> instance used to detect which parameters are services.</param>
9193
/// <returns>The <see cref="RequestDelegate"/>.</returns>
92-
public static RequestDelegate Create(MethodInfo methodInfo)
94+
public static RequestDelegate Create(MethodInfo methodInfo, IServiceProvider? serviceProvider)
9395
{
9496
if (methodInfo is null)
9597
{
9698
throw new ArgumentNullException(nameof(methodInfo));
9799
}
98100

99-
var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression: null);
101+
var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, serviceProvider, targetExpression: null);
100102

101103
return httpContext =>
102104
{
@@ -108,9 +110,10 @@ public static RequestDelegate Create(MethodInfo methodInfo)
108110
/// Creates a <see cref="RequestDelegate"/> implementation for <paramref name="methodInfo"/>.
109111
/// </summary>
110112
/// <param name="methodInfo">A request handler with any number of custom parameters that often produces a response with its return value.</param>
113+
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> instance used to detect which parameters are services.</param>
111114
/// <param name="targetFactory">Creates the <see langword="this"/> for the non-static method.</param>
112115
/// <returns>The <see cref="RequestDelegate"/>.</returns>
113-
public static RequestDelegate Create(MethodInfo methodInfo, Func<HttpContext, object> targetFactory)
116+
public static RequestDelegate Create(MethodInfo methodInfo, IServiceProvider? serviceProvider, Func<HttpContext, object> targetFactory)
114117
{
115118
if (methodInfo is null)
116119
{
@@ -128,15 +131,15 @@ public static RequestDelegate Create(MethodInfo methodInfo, Func<HttpContext, ob
128131
}
129132

130133
var targetExpression = Expression.Convert(TargetExpr, methodInfo.DeclaringType);
131-
var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, targetExpression);
134+
var targetableRequestDelegate = CreateTargetableRequestDelegate(methodInfo, serviceProvider, targetExpression);
132135

133136
return httpContext =>
134137
{
135138
return targetableRequestDelegate(targetFactory(httpContext), httpContext);
136139
};
137140
}
138141

139-
private static Func<object?, HttpContext, Task> CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression? targetExpression)
142+
private static Func<object?, HttpContext, Task> CreateTargetableRequestDelegate(MethodInfo methodInfo, IServiceProvider? serviceProvider, Expression? targetExpression)
140143
{
141144
// Non void return type
142145

@@ -154,7 +157,10 @@ public static RequestDelegate Create(MethodInfo methodInfo, Func<HttpContext, ob
154157
// return default;
155158
// }
156159

157-
var factoryContext = new FactoryContext();
160+
var factoryContext = new FactoryContext()
161+
{
162+
ServiceProvider = serviceProvider
163+
};
158164

159165
var arguments = CreateArguments(methodInfo.GetParameters(), factoryContext);
160166

@@ -234,6 +240,15 @@ private static Expression CreateArgument(ParameterInfo parameter, FactoryContext
234240
}
235241
else
236242
{
243+
if (factoryContext.ServiceProvider?.GetService<IServiceProviderIsService>() is IServiceProviderIsService serviceProviderIsService)
244+
{
245+
// If the parameter resolves as a service then get it from services
246+
if (serviceProviderIsService.IsService(parameter.ParameterType))
247+
{
248+
return Expression.Call(GetRequiredServiceMethod.MakeGenericMethod(parameter.ParameterType), RequestServicesExpr);
249+
}
250+
}
251+
237252
return BindParameterFromBody(parameter.ParameterType, allowEmpty: false, factoryContext);
238253
}
239254
}
@@ -788,6 +803,7 @@ private class FactoryContext
788803
{
789804
public Type? JsonRequestBodyType { get; set; }
790805
public bool AllowEmptyRequestBody { get; set; }
806+
public IServiceProvider? ServiceProvider { get; init; }
791807

792808
public bool UsingTempSourceString { get; set; }
793809
public List<(ParameterExpression, Expression)> TryParseParams { get; } = new();

0 commit comments

Comments
 (0)