【问题标题】:HttpClient retrieve all headersHttpClient 检索所有标头
【发布时间】:2013-04-25 14:29:32
【问题描述】:

目前,我正在研究 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 无法通过代码访问,因为这是私有字段。我只能通过“快速观看”窗口访问它。

这是我的请求:

var response = await _httpClient.PostAsync("/v3/oauth/request", content);

【问题讨论】:

    标签: c# http http-headers windows-phone-8 dotnet-httpclient


    【解决方案1】:

    好吧,HttpResponseMessage.Headers 返回一个HttpResponseHeaders 引用,所以你应该可以使用GetValues()

    string error = response.Headers.GetValues("X-Error").FirstOrDefault();
    string errorCode = response.Headers.GetValues("X-Error-Code").FirstOrDefault();
    

    【讨论】:

    • 不,我不能这样做,因为我会收到此错误Error 3Cannot apply indexing with [] to an expression of type System.Net.Http.Headers.HttpResponseHeaders'。为了清楚起见,我将更新上面的代码。
    • @ShulhiSapli:啊 - 没有发现它是HttpClient。将编辑。
    • 如果您需要更多详细信息,可以查看这里,github.com/shulhi/PocketNet/blob/dev/PocketNet/PocketNet/…
    【解决方案2】:

    由于问题的标题是“检索所有标题”,因此我想对此添加一个答案。

    HttpClient 方法返回的HttpResponseMessage 有两个标头属性:

    • HttpResponseMessage.Headers 是带有通用响应标头的 HttpResponseHeaders
    • HttpResponseMessage.Content.Headers 是一个 HttpContentHeaders,带有特定于内容的标头,例如 Content-Type

    两个对象都实现了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 的网络服务器)。

    生成所有标题的string

    我们可以使用单个 Linq 表达式生成一个扁平的标题字符串:

    • 使用Concat 组合HttpResponseMessage.HeadersHttpResponseMessage.Content.Headers
      • 不要使用Union,因为这样不会保留所有标题。
      • (作为个人风格偏好,当我将两个IEnumerable&lt;T&gt; 对象连接在一起时,我从Enumerable.Empty&lt;T&gt;() 开始以获得视觉上对称的结果 - 不是出于性能或任何其他原因)。
    • 在每个 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
            );
    

    【讨论】:

    • 这为 headers 变量创建了一个非常复杂的类型。如何将数据输出到字符串或简单的集合中?
    • @JamieMarshall 因为它将所有内容放入键/值对的 IEnumerable 中,您应该能够使用 foreach 循环来遍历组合列表。每对的key是头名,每对的值是头值的列表。
    • 值得注意的是,要使Concat 正常工作,您需要确保包含using System.Linq;
    【解决方案3】:

    只是我在尝试查找不存在的标头时发现的一个问题。您应该使用 TryGetValues 而不是 GetValues,因为在运行时如果找不到标头,它将引发异常。你会使用类似这样的代码:

    IEnumerable<string> cookieHeader; 
    response.Headers.TryGetValues("Set-Cookie", out cookieHeader);
    

    【讨论】:

      【解决方案4】:

      有点笨重,但简单易懂..

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

      【讨论】:

        【解决方案5】:

        这对我有用:

        (String[])response.Headers.GetValues("X-Error"))[0]
        

        【讨论】:

        • 我很好奇对 Nisse 投反对票的原因。我将它包含在我正在使用的控制台应用程序中。
        • 我也投了反对票,因为它不安全:HttpHeaders 的规范不保证 .GetValues(String) 将返回 String[](它只说它返回 IEnumerable&lt;String&gt;),如果有没有指定名称的值,那么您将得到一个InvalidOperationException - 如果重复相同的标题,那么这将只返回指定标题名称的第一个值。
        • 反对这个答案的另一个原因是因为 OP 询问如何获取“所有标题”,但这个答案只返回单个命名标题的值 - 所以这根本不能回答原始问题.
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-12
        • 1970-01-01
        • 2011-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多