Yeldar Kurmangaliyev
Yeldar Kurmangaliyev

Reputation: 34234

No service for type has been registered

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.

developer exception page screenshot

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

Answers (5)

Michał Turczyn
Michał Turczyn

Reputation: 37460

Update for .NET 8 and latest ASP.NET Core:

In order to register custom middleware we can use one of two options:

Here 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

Vitaliy Markitanov
Vitaliy Markitanov

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

sunnamed
sunnamed

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:

  • Remove RequestDelegate next from ctor of the Middleware
  • Add the IMiddleware interface (i.e public class MyMiddleware : IMiddleware)
public class MyMiddleware : IMiddleware
{
    public MyMiddleware(ILogger<MyMiddleware> logger)
    {
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        
    }
}
  • Add in Program.cs services.AddTransient<MyMiddleware>();
services.AddSerilog(logger);
services.AddHttpContextAccessor();
services.AddControllers().AddNewtonsoftJson();
services.AddEndpointsApiExplorer();
services.AddTransient<MyMiddleware>(); // this one
  • Add in Program.cs app.UseMiddleware<MyMiddleware>(); (I put it after the app.UseAuthorization();)
app.UseAuthorization();

app.UseMiddleware<GlobalExceptionHandlingMiddleware>(); // this one

app.MapControllers();

Upvotes: 3

davidfowl
davidfowl

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

Darjan Bogdan
Darjan Bogdan

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

Related Questions