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-ErrorX-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);

HttpClient检索所有标头

好吧,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.HeadersHttpResponseMessage.Content.Headers
    • 不要使用Union,因为它不会保留所有的头。
    • (作为个人风格偏好,当我将两个IEnumerable<T>对象连接在一起时,我从Enumerable.Empty<T>()开始,以获得视觉对称的结果-而不是性能或任何其他原因)。
  • 在连接它们的平面结果之前,在每个Headers集合上使用.SelectMany来平坦化每个集合
  • 使用AggregateStringBuilder有效地生成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 }}

它类似于输出的问题示例。因此,如果您希望将其显示为日志或输出的字符串,则可以随后连接字符串。