Search code examples
asp.net-coreasp.net-core-webapiasp.net-core-middleware

Why a middleware does not get executed if applied after app.UseEndpoints() in ASP.NET Core Web APIs


I am trying to create a middleware that should execute after the execution of endpoint. But when I add middleware in after the app.UseEndPoints(), it never gets invoked, however it is invoked if placed before app.UseEndPoints(), but that invokes it before execution of endpoint.

I tried to create a middleware, I placed if after app.UseEndPoints() middleware in the Configure method in the Startup class and expected it to be invoked after the execution of the endpoint (Web APIs action method), but it did not get invoked.


Solution

  • Middleware placed after app.UseEndpoints() usually doesn't execute because of how the ASP.NET Core request handling pipeline works. When a request comes in, it moves through each piece of middleware in the order they were added to the pipeline until it reaches app.UseEndpoints(). This particular middleware is responsible for matching the incoming request to a specific endpoint.

    Once a matching endpoint is found, app.UseEndpoints() invokes that endpoint to handle the request. After the endpoint finishes processing the request and generates a response, the response is sent back to the client immediately. This means the pipeline ends there, and any middleware added after app.UseEndpoints() is not executed because the response has already been dealt with, and there's no further processing required or expected for that request.

    To execute logic after an endpoint has processed a request, you can try any of the below option:

    • Structure your middleware so that it does any "after processing" logic after calling _next.Invoke(context) within the middleware. This part of the code will run after the downstream middleware, including the endpoint that app.UseEndpoints() invokes, has finished executing.
    • Use ASP.NET Core's filters (action filters, result filters) for specific functionality within the request life cycle of MVC or Razor Pages.
    • Use endpoint-specific middleware by applying it to a specific endpoint using Map or MapWhen.

    Below is the example how you can achieve requirement with custom middleware:

    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;
    
        public MyCustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            // Any logic here runs before the rest of the pipeline, e.g., before an endpoint is executed.
    
            await _next(context); // Call the next middleware in the pipeline.
    
            // Any logic here runs after the rest of the pipeline.
            // For instance, after the endpoint has executed and a response has been generated.
        }
    
    }
    

    In your Startup class’ Configure method, you insert your middleware before app.UseEndpoints:

    public void Configure(IApplicationBuilder app)
    {
        // ...
    
        // Add your middleware before UseEndpoints
        app.UseMiddleware<MyCustomMiddleware>();
    
        app.UseEndpoints(endpoints =>
        {
            // ...
        });
    
        // Don't add middleware here since it won't be executed normally after UseEndpoints
    }
    

    Here is the code with action filters:

    For action filters, you can create a custom class that inherits from IActionFilter or IAsyncResultFilter and then register that filter globally or on specific controllers or actions:

    public class MyCustomActionFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // Logic before the action executes.
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            // Logic after the action executes.
        }
    }
    

    Then register the filter in Startup (in ConfigureServices method):

    services.AddControllers(options => 
    {
        options.Filters.Add(new MyCustomActionFilter());
    });