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被拾取。

我對此感到驚喜,我希望它能幫助別人!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM