HTTP headers are small pieces of additional information in form of key/value pairs that travel around the internet. You can find them in virtually all HTTP requests and responses. Sooner or later any .NET developer will face the scenario where headers needs to be manipulated in a certain way. A very common task is to add headers to requests you might make with an HttpClient
and the good news is that manipulating request headers is really trivial since you are creating the entire request. On the other hand, manipulating response headers in ASP.Net Core might not be that easy. Don’t get me wrong, it’s something that we can do but there are some things to consider. So, let’s look a little bit deeper into what it takes to manipulate HTTP response headers in ASP.Net Core.
Where should we manipulate response headers?
The first question that naturally comes to mind when you get the task to manipulate response headers is: where in the application should you do it? Well, in ASP.Net Core the answer is in a certain way as natural as the question: in the middleware pipeline!
As all requests and responses necessarily pass through this pipeline, this is the best place where you can manipulate response headers. To do that, one would simply need to register an own middleware that does the desired response headers manipulation.
So, how would we do this? For response headers we would naturally think about where to place the response headers manipulation in relation to the next()
delegate. As we know this delegate is responsible to pass the request to the next middleware in the pipeline. The first thing that would come to mind is put the necessary logic for response headers manipulation after the next()
delegate because in this way we’ll catch the response and apply our logic once the response arrived in our middleware.
app.Use(async (context, next) =>
{
await next();
//Response header manipulation logic
});
However, this approach won’t work! You’ll probably get your code compiled but as soon as a request comes in and you try to manipulate the response headers you would get an InvalidOperationException
with the message that the headers are read-only and the response has already started. If we look at how a response is constructed, this error message is really meaningful. The response is nothing else then pieces of information placed in a specific order. First comes the status code, then come the headers and then comes the body. When the app arrives at the point to generate the response, it already starts to send parts of that response, like the status code and the headers, to the client. Even if the body is not ready yet to be sent.
Therefore, by the time the execution of your application hits your middleware, the first parts of the response are already started and on their way to the client that made the request. At this point, you can’t manipulate those parts of the response anymore. So, how can we fix this?
context.Response.OnStarting()
FTW!
Fortunately, ASP.Net Core has appropriate APIs to handle this scenario. Through the HttpContext
we can access the Response
an the Response
class has an OnStarting()
method that executes right before any of the response pieces are started and sent to the client. The method accepts a callback function with the specific purpose of performing whatever task must be performed before the body is written. In our case manipulating the response headers. Here’s how it could look like:
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers.Add("MyHeader", "GotItWorking!!!");
return Task.FromResult(0);
});
await next();
});
Also note that an OnStarting()
callback should always be registered in the first pass (so before the next delegate) of a middleware component. Consider that the terminating middleware (whatever form it may take) will very likely be writing some content to the body. Therefore, registering an OnStarting()
callback in the second pass (so after the next() delegate) is too late and will cause the above mentioned exception.
Changing the value of existing response headers
Very often you won’t need to add another header but just change the value of an existing one. A use case would be when you want to change the value of the location header that should be present on all your 201 HTTP responses. If you let Asp.Net Core generate this header for you (for instance when you are using the Created() IActionResult) it might have a value that it’s not appropriate. If your ASP.Net Core application is behind a reverse proxy then you might want to add the FQDN that the reverse proxy knows how to resolve as host in your location header and Asp.Net Core will certainly not be aware of it.
In this scenario just remember that the Headers
object on the response is a IHeaderDictionary
so you’ll be able to use dictionary specific methods to play around with values. Beware, however, that re-creating your own IHeaderDictionary
and adding it to the Response.Headers property is not an option since this will result in a compilation error saying that the collection is readonly and you can’t assign values to it. What I usually like to to is searching for the header I’m interested in (like the location header in the given use case), removing it from the collection and adding it with the value I need. This would result to some code similar to this:
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
int responseStatusCode = context.Response.StatusCode;
if (responseStatusCode == (int)HttpStatusCode.Created)
{
IHeaderDictionary headers = context.Response.Headers;
StringValues locationHeaderValue = string.Empty;
if (headers.TryGetValue("location", out locationHeaderValue))
{
context.Response.Headers.Remove("location");
context.Response.Headers.Add("location", "new location header value");
}
}
return Task.FromResult(0);
});
await next();
});
Please note that this code sample is heavily simplified for demo purpose.
Summary
So let’s sum up! Manipulating response headers in ASP.Net Core is something that you would need to do fairly often an there are some things you need to pay attention to. First of all, the right place to do this is in the middleware pipeline. Header manipulation needs to be done before a single byte of data is written to the body and therefore the right way to do it is to use the OnStarting()
method on the Response
object. Last but not least, it’s important that you provide the callback for the OnStarting()
method during the first pass through your middleware (so before the next()
delegate).
P.S: If you enjoyed this article, you might also want to check my YouTube channel for a lot more and visual .NET content. I also run a newsletter that’s different to any other newsletter as it focuses more on things that will help you become a better engineer, not just a better coder. You might want to subscribe to it as well!