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);
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 headersHttpResponseMessage.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).
string
of all headers:We can generate a flat string of headers using a single Linq expression:
Concat
to combine both HttpResponseMessage.Headers
and HttpResponseMessage.Content.Headers
.
Union
because that won't preserve all headers.IEnumerable<T>
objects together, I start off with Enumerable.Empty<T>()
for visually symmetrical results - not for performance or any other reason)..SelectMany
on each Headers collection to flatten each collection before concatenating their flat results.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()
);
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
);
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);
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With