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 an HttpResponseHeaders with generic response headers
  • HttpResponseMessage.Content.Headers is an HttpContentHeaders with content-specific headers like Content-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 both HttpResponseMessage.Headers and HttpResponseMessage.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 with Enumerable.Empty<T>() for visually symmetrical results - not for performance or any other reason).
  • Use .SelectMany on each Headers collection to flatten each collection before concatenating their flat results.
  • Use Aggregate with a StringBuilder to efficiently generate a string 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
        );