I saw some code lilke this:
public class CustomMiddleware {
private RequestDelegate next;
public CustomMiddleware (RequestDelegate nextDelegate) {
next = nextDelegate;
}
public async Task Invoke(HttpContext context, IResponseFormatter formatter) {
...
await next(context);
}
}
and IResponseFormatter service is registered as:
public void ConfigureServices(IServiceCollection services) {
services.AddTransient<IResponseFormatter, GuidService>();
}
I know how DI works, but my understanding how middleware works is, next
(RequestDelegate
) represents the next middleware's Invoke
method, so in CustomMiddleware, even the second argument is resolved by DI, but the definition of RequestDelegate
is
public delegate Task RequestDelegate(HttpContext context);
how does the previous middleware before CustomMiddleware
knows that CustomMiddleware
's Invoke
method has changed by having an extra argument? It cannot know in advance, therefore the previous middleware's next
RequestDelegate
does't match the signature of CustomMiddleware
's Invoke
method?
Internally, framework code uses reflection to determine arguments for both constructor and Invoke member of the middleware by the following convention
The middleware class must include:
- A public constructor with a parameter of type RequestDelegate.
- A public method named Invoke or InvokeAsync. This method must:
- Return a Task.
- Accept a first parameter of type HttpContext.
Additional parameters for the constructor and Invoke/InvokeAsync are populated by dependency injection (DI).
Reference Write custom ASP.NET Core middleware
As demonstrated from the Source code
/// <summary>
/// Adds a middleware type to the application's request pipeline.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
/// <param name="middleware">The middleware type.</param>
/// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, [DynamicallyAccessedMembers(MiddlewareAccessibility)] Type middleware, params object?[] args)
{
if (typeof(IMiddleware).IsAssignableFrom(middleware))
{
// IMiddleware doesn't support passing args directly since it's
// activated from the container
if (args.Length > 0)
{
throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
return UseMiddlewareInterface(app, middleware);
}
var applicationServices = app.ApplicationServices;
return app.Use(next =>
{
var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);
var invokeMethods = methods.Where(m =>
string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)
|| string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal)
).ToArray();
if (invokeMethods.Length > 1)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName));
}
if (invokeMethods.Length == 0)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName, middleware));
}
var methodInfo = invokeMethods[0];
if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType))
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task)));
}
var parameters = methodInfo.GetParameters();
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext))
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext)));
}
var ctorArgs = new object[args.Length + 1];
ctorArgs[0] = next;
Array.Copy(args, 0, ctorArgs, 1, args.Length);
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
if (parameters.Length == 1)
{
return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance);
}
var factory = Compile<object>(methodInfo, parameters);
return context =>
{
var serviceProvider = context.RequestServices ?? applicationServices;
if (serviceProvider == null)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}
return factory(instance, context, serviceProvider);
};
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With