Error when modifying response headers in middleware

617 views Asked by At

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?

2

There are 2 answers

2
Xinran Shen On BEST ANSWER

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

3
zabuli On

First of all as was mentioned in the comment, you have to put replacing header before calling this part:

await _next( context );

This part has to go at the end of the method.

Second point is, you cannot change your header as you are doing, since as it is written in the error, header is read-only. You can do workaround like this:

if (headers.TryGetValue("MyHeader", out test))
{
    context.Response.Headers.Remove("MyHeader");
}

context.Response.Headers.Add("MyHeader", "Test");