Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient retrieve all headers

Currently, I am working on API wrapper. If I send a bad Consumer Key, the server will return Status as 403 Forbidden in the header. It will also pass custom headers. How do I actually retrieve these custom headers?

This is the response receive from the server.

Cache-Control: private
Date: Wed,  01 May 2013 14:36:17 GMT
P3P: policyref="/w3c/p3p.xml",  CP="ALL CURa ADMa DEVa OUR IND UNI COM NAV INT STA PRE"
Server: Apache/2.2.23 (Amazon)
Status: 403 Forbidden
X-Error: Invalid consumer key.
X-Error-Code: 152
X-Powered-By: PHP/5.3.20
Connection: keep-alive

I need to retrieve the X-Error and X-Error-Code. Currently, I am using HttpClient class to process the request. If I watch the headers respond under Quick Watch in VS Studio 2012, I could find it like this

((System.Net.Http.Headers.HttpHeaders)(response.Headers)).headerStore["X-Error-Code"].ParsedValue

Is there any other way to do this?

Edit: headerStore is not accessible thru code as this is private field. I only get access to it through the Quick Watch window.

This is my snippet for the request:

var response = await _httpClient.PostAsync("/v3/oauth/request", content);
like image 572
Shulhi Sapli Avatar asked May 01 '13 15:05

Shulhi Sapli


4 Answers

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();
like image 133
Jon Skeet Avatar answered Oct 19 '22 07:10

Jon Skeet


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
        );
like image 38
Nick Avatar answered Oct 19 '22 08:10

Nick


Just a gotcha that I found when attempting to find a header that didn't exist. You should use TryGetValues instead of GetValues because at runtime it will throw an exception if the header is not found. You would use something like this code:

IEnumerable<string> cookieHeader; 
response.Headers.TryGetValues("Set-Cookie", out cookieHeader);
like image 13
Cameron Tinker Avatar answered Oct 19 '22 07:10

Cameron Tinker


A bit bulky, but simple to understand..

            System.Diagnostics.Debug.Write("----- CLIENT HEADERS -----" + Environment.NewLine);
            foreach (KeyValuePair<string, IEnumerable<string>> myHeader in myHttpClient.DefaultRequestHeaders)
            {
                System.Diagnostics.Debug.Write(myHeader.Key + Environment.NewLine);
                foreach(string myValue in myHeader.Value)
                {
                    System.Diagnostics.Debug.Write("\t" + myValue + Environment.NewLine);
                }
            }

            System.Diagnostics.Debug.Write("----- MESSAGE HEADERS -----" + Environment.NewLine);
            foreach (KeyValuePair<string, IEnumerable<string>> myHeader in myHttpRequestMessage.Headers)
            {
                System.Diagnostics.Debug.Write(myHeader.Key + Environment.NewLine);
                foreach (string myValue in myHeader.Value)
                {
                    System.Diagnostics.Debug.Write("\t" + myValue + Environment.NewLine);
                }
            }

            System.Diagnostics.Debug.Write("----- CONTENT HEADERS -----" + Environment.NewLine);
            foreach (KeyValuePair<string, IEnumerable<string>> myHeader in myHttpRequestMessage.Content.Headers)
            {
                System.Diagnostics.Debug.Write(myHeader.Key + Environment.NewLine);
                foreach (string myValue in myHeader.Value)
                {
                    System.Diagnostics.Debug.Write("\t" + myValue + Environment.NewLine);
                }
            }
like image 5
da_jokker Avatar answered Oct 19 '22 08:10

da_jokker