Search code examples
c#asp.net-mvc.net-core

Headers are read only exception when writing to HttpResponse


I have a controller that looks like this

IHttpWrite httpWrite;

[HttpPost]
public async Task<HttpResponse> Post(Request req)
{
    return await httpWrite.Write(req, Response);
}

that calls this

public async Task<HttpResponse> Write(object data, HttpResponse httpResponse)
{
    var json = JsonConvert.SerializeObject(data);

    httpResponse.OnStarting(() =>
    {
        httpResponse.Clear();
        httpResponse.ContentType = "application/json";
        return Task.CompletedTask;
    });

    await httpResponse.WriteAsync(json);
    return httpResponse;
}

But calling it gets this exception

System.InvalidOperationException: Headers are read-only, response has already started.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()

I understand you cannot set the headers once data is written to the response, but on my end I am setting the headers in the HttpResponse before writing anything to it.

From the debugging I've done, the exception for the headers is only being thrown after the controller returns the HttpResponse. This indicates to me the attempt to change the headers is being done on something outside of my code.

I'm using most of the boilerplate code Visual Studio sets up. This is the default Program

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
               .UseStartup<Startup>();

Which is calling the mostly-default Startup

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

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseMvc();
}

I believe the solution either lies in changing something in the configuration to not automatically set the response headers after I return the HttpResponse, or potentially returning a different object instead so that I do not have to manually set the headers. Without being able to know the source of what's trying to set the headers after I write to the HttpResponse, I am unsure how to proceed.


Solution

  • Why don't you just write your headers before writing the response? and even better: instead of manually set the response, why don't you declare the return as something more abstract and let the framework deal with these?

    anyway, here a piece of code on how to add custom headers:

    using System;
    using System.Linq;
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    
    namespace WebApplication.Controllers
    {
        [Route("api")]
        public class ValuesController : Controller
        {
            [HttpGet]
            [Route("values/{top}")]
            public IActionResult Get(int top)
            {
                // Generate dummy values
                var list = Enumerable.Range(0, DateTime.Now.Second)
                                 .Select(i => $"Value {i}")
                                 .ToList();
                list.Reverse();
    
                var result = new ObjectResult(list.Take(top))
                {
                    StatusCode = (int)HttpStatusCode.OK
                };
    
                Request.HttpContext.Response.Headers.Add("X-Total-Count", list.Count.ToString());
    
                return result;
            }
        }
    }