Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding custom middleware not working when using IMiddleware

I am trying to add a custom middleware to the pipeline (to be easier I will pick the .NET Core documentation example). Let's say we want to have the Spanish culture set whenever a call to API is fired. Here's the code which runs perfectly:

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        CultureInfo.CurrentCulture = new CultureInfo("es-ES");
        CultureInfo.CurrentUICulture = new CultureInfo("es-ES");

        // Call the next delegate/middleware in the pipeline
        await _next(context);
    }
}

public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

and the Startup class:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        //here is our custom middleware!
        app.UseRequestCulture();

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

That's fine, but as you can see, the RequestCultureMiddleware does not implement an interface or a base class/abstract class. You just need to remember when defining a middleware to create a constructor that receives the next middleware and also you need to create a method called specifically "InvokeAsync" with "HttpContext" as a parameter.

I tried to find a contract... a base class or an interface and guess what, we have "IMiddleware" which is part of "Microsoft.AspNetCore.Http" assembly. Wow, that's perfect. Let's implement it.

The interface looks like this:

namespace Microsoft.AspNetCore.Http
{
    //
    // Summary:
    //     Defines middleware that can be added to the application's request pipeline.
    public interface IMiddleware
    {
        //
        // Summary:
        //     Request handling method.
        //
        // Parameters:
        //   context:
        //     The Microsoft.AspNetCore.Http.HttpContext for the current request.
        //
        //   next:
        //     The delegate representing the remaining middleware in the request pipeline.
        //
        // Returns:
        //     A System.Threading.Tasks.Task that represents the execution of this middleware.
        Task InvokeAsync(HttpContext context, RequestDelegate next);
    }
}

And here is the implementation:

    public class RequestCultureMiddleware : IMiddleware
    {

        public Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            CultureInfo.CurrentCulture = new CultureInfo("es-ES");
            CultureInfo.CurrentUICulture = new CultureInfo("es-ES");

            // Call the next delegate/middleware in the pipeline
            return next(context);
        }
    }


    public static class RequestCultureMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestCulture(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestCultureMiddleware>();
        }
    }
}

But, when running the API I am getting the following error at run-time:

System.InvalidOperationException: No service for type 'WebApplication1.RequestCultureMiddleware' has been registered.
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.AspNetCore.Http.MiddlewareFactory.Create(Type middlewareType)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

How exactly I am supposed to register this middleware if not by using the extension "UseMiddleware"? Thanks.

like image 341
Dragos Stoica Avatar asked Jan 13 '20 09:01

Dragos Stoica


People also ask

How do I add middleware to the application request pipeline?

Now, we need to add our custom middleware in the request pipeline by using Use extension method as shown below. We can add middleware using app. UseMiddleware<MyMiddleware>() method of IApplicationBuilder also. Thus, we can add custom middleware in the ASP.NET Core application.

How does middleware work in NET Core?

Middleware are software components that are assembled into an application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline.

What is the use of MAP extension while adding middleware to ASP.NET Core pipeline?

The Map extension method is used to match request delegates based on a request's path. Map simply accepts a path and a function that configures a separate middleware pipeline. In the following example, any request with the base path of /maptest will be handled by the pipeline configured in the HandleMapTest method.

What is the difference between IApplicationBuilder use () and IApplicationBuilder run ()?

Run() is an extension method on IApplicationBuilder instance which adds a terminal middleware to the application's request pipeline. The Run method is an extension method on IApplicationBuilder and accepts a parameter of RequestDelegate.


2 Answers

I'm sure this problem has been solved long ago after 5 months, but I'm writing this advice just in case.

The problem is the "InvokeAsync" method of your custom middleware program is not be executed even though you built in it in "Configure" method of Startup.

I had the same problem the other day and solved it by, but I putting built in code right before the app.UseEndpoints method.

in your case

app.UseAuthorization();
app.UseRequestCulture();  // <- this way.
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

By the way, if you put it after the app.UseEndpoints method, the constructor will be called, but the InvokeAsync method will not be executed.

like image 145
Masatoshi Furuya Avatar answered Oct 18 '22 04:10

Masatoshi Furuya


You're using factory-based middleware. As described in those docs, you've missed an important step:

... the IMiddlewareFactory instance registered in the container is used to resolve the IMiddleware implementation instead of using the convention-based middleware activation logic. The middleware is registered as a scoped or transient service in the app's service container.

In your case, that registration would look something like this:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<RequestCultureMiddleware>();
}
like image 41
Kirk Larkin Avatar answered Oct 18 '22 03:10

Kirk Larkin