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 manipulate 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. Manipulating the 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 do this?
The first question that naturally comes to mind when you get the task to manipulate response headers is: where in the application should I o it? Well, in ASP.Net Core the answer is in a certain way as natural as the question: in the middleware! I have wrote an introductory article into middleware in ASP.Net Core, so if you are not familiar with the concept, you might want to check it out. As a baseline, however, we should note that ASP.Net Core comes with a request pipeline and middleware are just pieces of software that are part of this pipeline. Each request will go through this pipeline, as well as all responses.
As all requests and 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.
When should we do the manipulation?
This is the trickier part! As said manipulating incoming request headers is no big deal. 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.
1 2 3 4 5 | app.Use(async (context, next) => { await next(); //Response header manipulation logic }); |
However, THIS IS WRONG!!! If you proceed this way, 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 readonly 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. So if Asp.Net Core has started to write even one byte of information in the body, you cannot change the previous information anymore. This means of course that you would have to manipulate the response headers before anything else starts to write the body of the response. So how to do that?
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 starting to write anything in the boy of the request. 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:
1 2 3 4 5 6 7 8 9 | 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.
What if I want to change the value of an existing header?
That’s a fair question since a lot of times 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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. 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).
I really hope that this article will prove useful to some people. If you have anything to add or to ask, don’t be shy and drop a comment. And if you really think this article is useful also don’t be shy and share it. Cheers!
How useful was this post?
Click on a star to rate it!
Average rating / 5. Vote count:
Dan Patrascu-Baba
Latest posts by Dan Patrascu-Baba (see all)
- Configuration and environments in ASP.NET Core - 25/11/2019
- GraphQL in the .NET ecosystem - 19/11/2019
- A common use case of delegating handlers in ASP.NET API - 12/11/2019