Reputation: 34234
I am trying to implement ASP.NET Core middleware, and this is the whole code I have in my project:
public class HostMiddleware : IMiddleware
{
public int Count { get; set; }
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.Request.Query.ContainsKey("hello"))
{
await context.Response.WriteAsync($"Hello World: {++Count}");
}
else
{
await next.Invoke(context);
}
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<HostMiddleware>();
app.Run(async context =>
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Bad request.");
});
}
However, when I run this server I get the following error:
InvalidOperationException: No service for type 'WebApplication4.HostMiddleware' has been registered.
Why do I get this error? Why would my middleware need to register any services if I don't use dependency injection in my project?
Update:
For some reason, this error does not occur when I stop using IMiddleware
, rename InvokeAsync
to Invoke
and implement my middleware in the following way:
public class WmsHostMiddleware
{
private readonly RequestDelegate _next;
public int Count { get; set; }
public WmsHostMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Query.ContainsKey("hello"))
{
await context.Response.WriteAsync($"Hello World: {++Count}");
}
else
{
await _next.Invoke(context);
}
}
}
The question is still open - why does this happen? What is the difference? Why do I need to register services when I use IMiddleware
.
Upvotes: 18
Views: 41076
Reputation: 37460
Update for .NET 8 and latest ASP.NET Core:
In order to register custom middleware we can use one of two options:
WebApplication.Use(Func<RequestDelegate,RequestDelegate>)
methodHere is small example with both examples:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.Use(async (context, next) =>
{
Console.WriteLine("hello world");
await next();
});
app.UseMiddleware<MyMiddleware>();
app.UseHttpsRedirection();
app.MapGet("/", () =>
{
})
.WithOpenApi();
app.Run();
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine("hello world from middleware class");
await _next(context);
}
}
We no longer require to register middleware class.
References:
Upvotes: 0
Reputation: 2458
To elaborate more on the details of why you need to register service into the DI system if you use middleware implemented via IMiddleware please look at the source code of the middleware - search for the file Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.cs
.
In short, UseMiddleware
extension method checks how you implemented your middleware (by this condition: if (typeof(IMiddleware).IsAssignableFrom(middleware))
) and if your implementation is of type IMiddleware, logic is to use internal class InterfaceMiddlewareBinder
to create an instance of your middleware, what is in turn is using _serviceProvider.GetRequiredService(middlewareType) as IMiddleware;
to find your service in the DI container.
As result, you have to register your implementation in the DI container. Microsoft has documentation about this here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/extensibility?view=aspnetcore-8.0
When you implement your middleware using a conventional approach, reflection is used and you don't need to register your middleware in DI.
Upvotes: 0
Reputation: 233
It's still required to be made in 2023 of ASP.NET 6.5.0
with .NET 7
This is very weird because sometimes I could run my app without exception and time by the time it sends exceptions on the app start
My solution was:
RequestDelegate next
from ctor of the Middlewarepublic class MyMiddleware : IMiddleware
)public class MyMiddleware : IMiddleware
{
public MyMiddleware(ILogger<MyMiddleware> logger)
{
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
}
}
Program.cs
services.AddTransient<MyMiddleware>();
services.AddSerilog(logger);
services.AddHttpContextAccessor();
services.AddControllers().AddNewtonsoftJson();
services.AddEndpointsApiExplorer();
services.AddTransient<MyMiddleware>(); // this one
Program.cs
app.UseMiddleware<MyMiddleware>();
(I put it after the app.UseAuthorization();
)app.UseAuthorization();
app.UseMiddleware<GlobalExceptionHandlingMiddleware>(); // this one
app.MapControllers();
Upvotes: 3
Reputation: 38834
Today, when you use the IMiddleware interface, you also have to add it to the dependency injection container as a service:
services.AddTransient<HostMiddleware>();
Upvotes: 45
Reputation: 3900
Implementation of UseMiddleware
extension method uses container to activate instances of middlewares which implement IMiddleware
, if that's not the case it will try to create instance using Activator.CreateInstance
and additional ctor params which you may pass in UseMiddleware
method.
You can take a look at the source code
Upvotes: 8