bluedot
bluedot

Reputation: 782

Set response header in middleware when using ActionResult

I have an asp .net core web api where I need to set some headers in the response. This based on the body of the response. (calculate md5)

I would like to do this in a custom middleware so that every request will have the same response headers.

I tried the following setup:

First I have a controller with a simple endpoint:

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    [HttpGet]
    [Route("Test")]
    public IActionResult DoTest()
    {
        var result = new { someProp = "somePropValue" };
        return Ok(result);
    }
}

I added a custom middleware class:

public class CustomResponseHandler
{
    private readonly RequestDelegate _next;

    public CustomResponseHandler(RequestDelegate next)
    {
        _next = next;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        await _next(context);
        context.Response.Headers.Add("foo", "fii");
    }
}

In startup I added this middleware using the Configure method:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();

        app.UseMiddleware<CustomResponseHandler>();

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

When I run this, the context.Response.Headers.Add("foo", "fii"); line does get executed.

It also executes after returning the result from the endpoint which is perfect.

However, when I look at the response, the header "foo" is still missing.

Is there a way to achieve this.

I noticed I do get the header when I add it before returning the result in the endpoint:

    public IActionResult DoTest()
    {
        var result = new { someProp = "somePropValue" };
        Response.Headers.Add("foo", "fii");
        return Ok(result);
    }

Upvotes: 1

Views: 2575

Answers (1)

scharnyw
scharnyw

Reputation: 2686

By the time next() has executed in your middleware, the server has already started sending the response to the client. This means that headers can not be modified anymore. Quote from this Microsoft Docs:

Don't call next.Invoke after the response has been sent to the client. Changes to HttpResponse after the response has started throw an exception. For example, setting headers and a status code throw an exception. Writing to the response body after calling next:

  • May cause a protocol violation. For example, writing more than the stated Content-Length.

  • May corrupt the body format. For example, writing an HTML footer to a CSS file.

What you should do is to either add the headers before calling next(), or add it in a hook in Response.OnStarting:

public async Task InvokeAsync(HttpContext context)
{
    context.Response.OnStarting(() =>
    {
        context.Response.Headers.Add("foo", "fii");
        return Task.CompletedTask;
    });
    await next();
}

Upvotes: 2

Related Questions