HttpClient检索所有标头
本文关键字:检索 HttpClient | 更新日期: 2023-09-27 18:11:36
目前,我正在开发API包装器。如果我发送一个坏的Consumer Key
,服务器将返回Status
作为头中的403 Forbidden
。它还将传递自定义头。我如何实际检索这些自定义头?
这是从服务器接收到的响应。
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
我需要检索X-Error
和X-Error-Code
。目前,我正在使用HttpClient
类来处理请求。如果我在VS Studio 2012的Quick watch下观看标题响应,我可以找到这样的
((System.Net.Http.Headers.HttpHeaders)(response.Headers)).headerStore["X-Error-Code"].ParsedValue
还有其他方法吗?
编辑:headerStore
不能通过代码访问,因为这是私有字段。我只能通过Quick Watch窗口访问它。
这是我的请求代码片段:
var response = await _httpClient.PostAsync("/v3/oauth/request", content);
好吧,HttpResponseMessage.Headers
返回一个HttpResponseHeaders
引用,所以你应该能够使用GetValues()
string error = response.Headers.GetValues("X-Error").FirstOrDefault();
string errorCode = response.Headers.GetValues("X-Error-Code").FirstOrDefault();
既然问题的标题是"检索所有标题",我想添加一个关于这个的答案。
HttpClient
方法返回的HttpResponseMessage
有两个头属性:
-
HttpResponseMessage.Headers
是具有通用响应头的HttpResponseHeaders
-
HttpResponseMessage.Content.Headers
是具有Content-Type
等内容特定头的
HttpContentHeaders
。这两个对象都实现了IEnumerable<KeyValuePair<string, IEnumerable<string>>
,所以你可以很容易地把所有的头文件像这样组合起来:
var responseMessage = await httpClient.GetAsync(url);
var headers = responseMessage.Headers.Concat(responseMessage.Content.Headers);
// headers has type IEnumerable<KeyValuePair<String,IEnumerable<String>>>
它是一个可枚举的多值名称集的原因是一些 HTTP头(如Set-Cookie
)可以在响应中重复(即使大多数其他头只能出现一次-但是软件应该优雅地处理违反rfc的web服务器返回无效头)。
生成所有标头的string
:
可以使用一个Linq表达式生成一个平面的头文件字符串:
- 使用
Concat
来组合HttpResponseMessage.Headers
和HttpResponseMessage.Content.Headers
。- 不要使用
Union
,因为它不会保留所有的头。 - (作为个人风格偏好,当我将两个
IEnumerable<T>
对象连接在一起时,我从Enumerable.Empty<T>()
开始,以获得视觉对称的结果-而不是性能或任何其他原因)。
- 不要使用
- 在连接它们的平面结果之前,在每个Headers集合上使用
.SelectMany
来平坦化每个集合。 - 使用
Aggregate
和StringBuilder
有效地生成string
表示。
一样:
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
:
另一种选择是使用。net Framework 1.1中的经典NameValueCollection
类,它支持具有多个值的键(实际上,它在经典ASP中使用)。. NET WebForms用于此目的):
一样:
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
);
只是我在试图找到一个不存在的标题时发现的一个问题。你应该使用TryGetValues而不是GetValues,因为在运行时,如果没有找到标头,它会抛出异常。您可以使用如下代码:
IEnumerable<string> cookieHeader;
response.Headers.TryGetValues("Set-Cookie", out cookieHeader);
有点笨重,但很容易理解…
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);
}
}
这个适合我:
(String[])response.Headers.GetValues("X-Error"))[0]
这是一个请求,但响应也有相同的头结构。在。net 6中测试
somerequest.Headers.Select(x => new { x.Key, Value = x.Value?.FirstOrDefault() } )
在即时窗口中显示为:
{System.Linq.Enumerable.SelectEnumerableIterator<System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IEnumerable<string>>, <>f__AnonymousType0<string, string>>}
[0]: {{ Key = Transfer-Encoding, Value = chunked }}
[1]: {{ Key = Server, Value = Microsoft-IIS/10.0 }}
[2]: {{ Key = WWW-Authenticate, Value = Bearer error="invalid_token", error_description="The token expired at '03/29/2023 11:37:34'" }}
[3]: {{ Key = X-Powered-By, Value = ASP.NET }}
[4]: {{ Key = Date, Value = Thu, 30 Mar 2023 10:23:47 GMT }}
它类似于输出的问题示例。因此,如果您希望将其显示为日志或输出的字符串,则可以随后连接字符串。