Yesterday I talked about a problem we encountered when trying to inject a scoped service into ASP.NET Core middleware.
The middleware used was convention based middleware, one of the ways to construct middleware in ASP.NET Core.
With convention based middleware there isnāt a base class or interface to implement. The only rules your middleware class needs to apply to are:
- It has a public constructor with a parameter of type RequestDelegate.
- It contains a public method named
Invoke
orInvokeAsync
. This method must:- Return a
Task
. - Accept a first parameter of type HttpContext.
- Return a
Convention based middleware is registered with a singleton lifetime, so you can only constructor injection for the dependencies with a singleton lifetime. For transient and scoped dependencies you need to add extra parameters to the InvokeAsync()
method:
public async Task InvokeAsync(HttpContext context, ILogger logger) | |
{ | |
} |
There is a log of magic going on when using convention based middleware and it shouldnāt be a surprise that people are not aware of its behavior.
If you want full control on the middleware construction and avoid any kind of convention-based āmagicā you can use the factory based middleware instead.
Now we need a class that implements the IMiddleware interface:
public class RequestResponseLoggingMiddleware: IMiddleware | |
{ | |
public async Task InvokeAsync(HttpContext context, RequestDelegate next) | |
{ | |
// Call the next delegate/middleware in the pipeline. | |
await next(context); | |
} | |
} |
A transient or scoped dependency can now be injected through the constructor:
public class RequestResponseLoggingMiddleware: IMiddleware | |
{ | |
private readonly IApiLoggingService _apiLoggingService; | |
public RequestResponseLoggingMiddleware(IApiLoggingService apiLoggingService) | |
{ | |
_apiLoggingService=apiLoggingService; | |
} | |
public async Task InvokeAsync(HttpContext context, RequestDelegate next) | |
{ | |
// Call the next delegate/middleware in the pipeline. | |
await next(context); | |
} | |
} |
The only thing remaining to do is to register our factory based middleware with a transient lifetime:
var builder = WebApplication.CreateBuilder(args); | |
builder.Services.AddTransient<RequestResponseLoggingMiddleware>(); |