zoukankan      html  css  js  c++  java
  • HttpClientFactory GET请求需要验证Content-Type的,无解

    这边要对接一个get请求的api接口,其接口必须传

    Content-Type 值 为application/vnd.api+json

    否则获取不到数据。

    据官方文档介绍。
    Content-Type 为entity类型只能在request.Content.heads里面设置
    特么的,真垃圾,用HttpClient 去调用 GET请求需要验证Content-Type的,无解!

     

     

     以下代码需要两个包:Microsoft.Extensions.Http和Microsoft.Extensions.DependencyInjection。

    string testapiurl="xx";

     

     

      ServiceCollection ServiceCollection = new ServiceCollection();
                ServiceCollection.AddHttpClient("github", c =>
                {
                    c.BaseAddress = new Uri("https://api.github.com/");
                    c.DefaultRequestHeaders.Add("Accept", "application/vnd.api+json");
                    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
                });
                ServiceCollection.AddHttpClient("github2", c =>
                {
                    c.BaseAddress = new Uri("https://api.github.com/");
                    c.DefaultRequestHeaders.Add("Accept", "application/vnd.api+json");
                    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
                });
                var serviceProvider = ServiceCollection.BuildServiceProvider(); ;
                var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Restart();
                using (var client = httpClientFactory.CreateClient("github"))
                {
                    for (int i = 0; i < 100; i++)
                    {
                        using (var request = new HttpRequestMessage())
                        {
                            request.Method = new HttpMethod("GET");
                            request.RequestUri = new Uri(testapiurl);
                            request.Content = new StringContent(string.Empty);
                            request.Content.Headers.Clear();
                            request.Content.Headers.Add("Content-Type" , "application/vnd.api+json");
                            using (var response = await client.SendAsync(request))
                            {
                                var content = await response.Content.ReadAsStringAsync();
    
                                Console.WriteLine(content);
                            };
                        }
    
                    }
                }

     

    会移除
    Content-Type添加的东西,然后设置为
    "Content-Type", "text/xml"

    var field = typeof(System.Net.Http.Headers.HttpRequestHeaders).GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) ?? typeof(System.Net.Http.Headers.HttpRequestHeaders).GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); if (field != null) { var invalidFields = (HashSet<string>)field.GetValue(null); invalidFields.Remove("Content-Type"); }
    _client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml");

    只能在具有内容的请求(例如POST , PUT等)上指定Content-Type标题,被强了啊

     

    因此,那get 请求的api需要验证Content-Type的搞卵啊,这什么设计啊


    某某似乎也遇到同样的问题。

     

    
    

    我正在尝试设置一个HttpClient对象的Content-Type头,如我所调用的API所要求的。

    
    

    我尝试设置Content-Type如下所示:

    
    
     using (var httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri("http://example.com/"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json"); // ... } 
    
    

    它允许我添加Accept头,但是当我尝试添加Content-Type ,会抛出以下exception:

    
    

    错误的标题名称。 确保请求头与HttpRequestMessage一起使用,使用HttpResponseMessage响应头和使用HttpContent对象的内容头。

    
    

    如何在HttpClient请求中设置Content-Type标头?

    
    

     

    内容types是内容的头部,而不是请求的头部,这就是失败的原因。 Robert Levybuild议的AddWithoutValidation可以工作,但是您也可以在创build请求内容时自己设置内容types(请注意,代码段在两个地方添加了“application / json”(对于Accept和Content-Type标头):

     HttpClient client = new HttpClient(); client.BaseAddress = new Uri("http://example.com/"); client.DefaultRequestHeaders .Accept .Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "relativeAddress"); request.Content = new StringContent("{"name":"John Doe","age":33}", Encoding.UTF8, "application/json");//CONTENT-TYPE header client.SendAsync(request) .ContinueWith(responseTask => { Console.WriteLine("Response: {0}", responseTask.Result); }); 

    对于那些没有看到约翰对卡洛斯解决scheme的评论…

     req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 

    如果你不介意一个小的库依赖, Flurl.Http [披露:我是作者]使这个超级简单。 它的PostJsonAsync方法负责序列化内容和设置content-type头,而ReceiveJson反序列化响应。 如果需要accept头文件,则需要自己设置,但Flurl也提供了一个相当干净的方法:

     using Flurl.Http; var result = await "http://example.com/" .WithHeader("Accept", "application/json") .PostJsonAsync(new { ... }) .ReceiveJson<TResult>(); 

    Flurl使用HttpClient和Json.NET,它是一个PCL,所以它可以在各种平台上工作。

     PM> Install-Package Flurl.Http 

    尝试使用TryAddWithoutValidation

      var client = new HttpClient(); client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8"); 

    调用AddWithoutValidation而不是Add (请参阅此MSDN链接 )。

    另外,我猜你正在使用的API实际上只需要POST或PUT请求(不是普通的GET请求)。 在这种情况下,当您调用HttpClient.PostAsync并传入HttpContent ,请在该HttpContent对象的Headers属性上进行设置。

    .Net试图强迫你遵守某些标准,即只能在具有内容的请求(例如POST , PUT等)上指定Content-Type标题。 因此,正如其他人已经指出的,设置Content-Type头的首选方法是通过HttpContent.Headers.ContentType属性。

    这就是说,某些API(如2016-12-19的LiquidFiles Api )需要为GET请求设置Content-Type标头。 .Net将不允许在请求本身上设置这个头文件 – 即使使用TryAddWithoutValidation 。 此外,您不能为请求指定一个Content – 即使它是零长度的。 我似乎能解决这个问题的唯一方法就是去反思。 代码(以防其他人需要它)是

     var field = typeof(System.Net.Http.Headers.HttpRequestHeaders) .GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) ?? typeof(System.Net.Http.Headers.HttpRequestHeaders) .GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); if (field != null) { var invalidFields = (HashSet<string>)field.GetValue(null); invalidFields.Remove("Content-Type"); } _client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml"); 

    编辑:

    如注释中所述,该字段在不同版本的dll中具有不同的名称。 在GitHub的源代码中 ,这个字段目前被命名为s_invalidHeaders 。 根据@David Thompson的build议,这个例子已经被修改来解释这个。

    一些关于.NET Core的额外信息(在阅读erdomke关于设置私有字段以在没有内容的请求上提供内容types的post之后)…

    在debugging我的代码后,我看不到通过reflection设置的私有字段 – 所以我想我会尝试重新创build问题。

    我已经尝试使用.Net 4.6的以下代码:

     HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, @"myUrl"); httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json"); HttpClient client = new HttpClient(); Task<HttpResponseMessage> response = client.SendAsync(httpRequest); //I know I should have used async/await here! var result = response.Result; 

    而且,正如预期的那样,我收到了一个聚合exception,其内容是"Cannot send a content-body with this verb-type."

    但是,如果我用.NET Core(1.1)做同样的事情 – 我没有得到一个例外。 我的请求很高兴地被我的服务器应用程序回答,内容types被拾取。

    我对此感到惊喜,我希望它能帮助别人!

     
  • 相关阅读:
    Codeforces Round #688 (Div. 2)B. Suffix Operations(思维+贡献)
    Codeforces Round #688 (Div. 2)C. Triangles(思维+贪心)
    Codeforces Round #689 (Div. 2, based on Zed Code Competition)D. Divide and Summarize(分治+dfs)
    Codeforces Round #689 (Div. 2, based on Zed Code Competition)C. Random Events(思维)
    Codeforces Round #689 (Div. 2, based on Zed Code Competition)B. Find the Spruce(dp + 重复利用)
    Educational Codeforces Round 101 (Rated for Div. 2)D. Ceil Divisions(思维)
    Educational Codeforces Round 101 (Rated for Div. 2)C. Building a Fence(思维+区间处理)
    Codeforces Round #693 (Div. 3)G. Moving to the Capital(dfs)
    Codeforces Round #693 (Div. 3)E. Correct Placement
    Git 学习笔记02丨Git 分支
  • 原文地址:https://www.cnblogs.com/tofight/p/15130981.html
Copyright © 2011-2022 走看看