HttpClient retrieve all headers
Well, HttpResponseMessage.Headers
returns an HttpResponseHeaders
reference, so you should be able to use GetValues()
string error = response.Headers.GetValues("X-Error").FirstOrDefault();
string errorCode = response.Headers.GetValues("X-Error-Code").FirstOrDefault();
Since the title of the question is "retrieve all headers", I wanted to add an answer in regards to that.
The HttpResponseMessage
returned by HttpClient
methods has two header properties:
HttpResponseMessage.Headers
is anHttpResponseHeaders
with generic response headersHttpResponseMessage.Content.Headers
is anHttpContentHeaders
with content-specific headers likeContent-Type
Both objects implement IEnumerable<KeyValuePair<string, IEnumerable<string>>
, so you can easily combine all the headers with something like this:
var responseMessage = await httpClient.GetAsync(url);
var headers = responseMessage.Headers.Concat(responseMessage.Content.Headers);
// headers has type IEnumerable<KeyValuePair<String,IEnumerable<String>>>
The reason it's an-enumerable-set-of-names-with-multiple-values is because some HTTP headers (like Set-Cookie
) can be repeated in a response (even though the majority of other headers can only appear once - but software should gracefully handle an RFC-violating webserver returning invalid headers).
Generating a string
of all headers:
We can generate a flat string of headers using a single Linq expression:
- Use
Concat
to combine bothHttpResponseMessage.Headers
andHttpResponseMessage.Content.Headers
.- Don't use
Union
because that won't preserve all headers. - (As a personal style preference, when I'm concatenating two
IEnumerable<T>
objects together, I start off withEnumerable.Empty<T>()
for visually symmetrical results - not for performance or any other reason).
- Don't use
- Use
.SelectMany
on each Headers collection to flatten each collection before concatenating their flat results. - Use
Aggregate
with aStringBuilder
to efficiently generate astring
representation.
Like so:
HttpResponseMessage resp = await httpClient.GetAsync( url );
String allHeaders = Enumerable
.Empty<(String name, String value)>()
// Add the main Response headers as a flat list of value-tuples with potentially duplicate `name` values:
.Concat(
resp.Headers
.SelectMany( kvp => kvp.Value
.Select( v => ( name: kvp.Key, value: v ) )
)
)
// Concat with the content-specific headers as a flat list of value-tuples with potentially duplicate `name` values:
.Concat(
resp.Content.Headers
.SelectMany( kvp => kvp.Value
.Select( v => ( name: kvp.Key, value: v ) )
)
)
// Render to a string:
.Aggregate(
seed: new StringBuilder(),
func: ( sb, pair ) => sb.Append( pair.name ).Append( ": " ).Append( pair.value ).AppendLine(),
resultSelector: sb => sb.ToString()
);
Loading all headers into a NameValueCollection
:
Another alternative is to use the classic NameValueCollection
class from .NET Framework 1.1, which supports keys with multiple values (indeed, it's used in Classic ASP.NET WebForms for this purpose):
Like so:
HttpResponseMessage resp = await httpClient.GetAsync( url );
NameValueCollection allHeaders = Enumerable
.Empty<(String name, String value)>()
// Add the main Response headers as a flat list of value-tuples with potentially duplicate `name` values:
.Concat(
resp.Headers
.SelectMany( kvp => kvp.Value
.Select( v => ( name: kvp.Key, value: v ) )
)
)
// Concat with the content-specific headers as a flat list of value-tuples with potentially duplicate `name` values:
.Concat(
resp.Content.Headers
.SelectMany( kvp => kvp.Value
.Select( v => ( name: kvp.Key, value: v ) )
)
)
.Aggregate(
seed: new NameValueCollection(),
func: ( nvc, pair ) => { nvc.Add( pair.name, pair.value ); return nvc; },
resultSelector: nvc => nvc
);