Search code examples
c#asp.net-corehttp-headersasp.net-core-middlewarehttpresponsemessage

Error when modifying response headers in middleware


I'm trying to modify the response headers in some middleware after it has finished in the controller, but I'm getting an error message:

System.InvalidOperationException: Headers are read-only, response has already started.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
   at EditResponseMiddleware.InvokeAsync(HttpContext context) in

The controller is the default WeatherForecast controller, and is not included here.

This is the code that triggers the exception:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseMiddleware<EditResponseMiddleware>();

app.UseAuthorization();

app.MapControllers();

app.Run();

public class EditResponseMiddleware
{
    private readonly RequestDelegate _next;

    public EditResponseMiddleware( RequestDelegate next )
    {
        _next = next;
    }
    
    public async Task InvokeAsync( HttpContext context )
    {
        await _next( context );
        
        context.Response.Headers["MyHeader"] = "Test";
    }
}

Is there a workaround for this, or are we not meant to modify any part of the response at this point?


Solution

  • Response headers can't be set after anything has been written to the response body.Once you pass the request to next middleware and it writes to the Response, then the Middleware can't set the Response headers again.

    Here is an OnStarting method that Add a delegate to be invoked just before response headers will be sent to the client. Callbacks registered here run in reverse order. So you can change your code to:

    public async Task InvokeAsync(HttpContext context)
            {
                context.Response.OnStarting(() =>
                {             
                    context.Response.Headers["MyHeader"] = "Test";
                    return Task.CompletedTask;
                });
    
                await _next(context);       
            }
    

    enter image description here