.NET Core HttpClient調用騰訊雲對象存儲Web API的"ERROR_CGI_PARAM_NO_SUCH_OP"問題

開門見山地說一下問題的緣由:調用 web api 時請求頭中多了雙引號,請求體中少了雙引號。html

騰訊雲提供的對象存儲(COS)C# SDK 是基於 .NET Framework 用 WebRequest 實現的,咱們直接將這個實現遷移到 .NET Core 是能夠正常調用,但後來咱們基於 HttpClient 實現,調用 web api 時老是返回 "ERROR_CGI_PARAM_NO_SUCH_OP" 錯誤。web

用 Wireshark 抓包後發現,基於 WebRequest 的實現的請求包開頭比基於 HttpClient 的實現多了個 "Preamble: 0d0a"。json

1)基於 WebRequest 的實現api

2)基於 HttpClient 的實現app

檢查代碼後發現,在構建 multipart/form-data 時,騰訊雲官方基於 WebRequest 的實現是這樣構建數據包的開頭的:url

var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
var beginBoundary = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

咱們基於 HttpClient 的實現用的是 MultipartFormDataContent :spa

var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
var data = new MultipartFormDataContent(boundary);

前者構建的 Multipart 數據包比後者多出了 \r\n (回車換行),而 0d0a 正是 \r\n 的 ASCII 碼。根據 Multipart Content-Type 規範,這個多出來的 \r\n 是多餘的,因此被解析爲  "Preamble: 0d0a" 。code

因而修改基於 HttpClient 的實現,也加上這個額外的 \r\n :orm

var ms = new MemoryStream();
var bytes = Encoding.UTF8.GetBytes("\r\n");
ms.Write(bytes, 0, bytes.Length);
(await data.ReadAsStreamAsync()).CopyTo(ms);
ms.Position = 0;
var sc = new StreamContent(ms);
sc.Headers.ContentType = data.Headers.ContentType;
request.Content = sc;

但加上後依然是"ERROR_CGI_PARAM_NO_SUCH_OP"錯誤(實際上不加開頭的 \r\n 也不要緊,問題與這個無關)。htm

繼續仔細對比抓包,發現 HttpClient 的實現中 form-data 部署少了雙引號,好比 name=op ,基於 WebRequest 的實現用的是 name="op"

但加上後依舊是"ERROR_CGI_PARAM_NO_SUCH_OP"錯誤。

再繼續對比抓包,發現 HttpClient 的實現這 Content-Type 中比 WebRequest 的實現多了2個雙引號

1) Content-Type: multipart/form-data; boundary="---------------8d5289300ea3a0d"  

2)  Content-Type: multipart/form-data; boundary=---------------8d527aeed341201 

去找這2個雙引號以後,問題終於解決了。

最終基於 .NET Core HttpClient 的實現代碼以下("Preamble: 0d0a"沒有影響,不須要加):

var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Authorization", signature);

var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
var data = new MultipartFormDataContent(boundary);
data.Add(new ByteArrayContent(Encoding.UTF8.GetBytes("upload")), "\"op\"");

var streamContent = new StreamContent(uploadStream);
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
    Name = "\"fileContent\"",
    FileName = "\"" + fileName + "\""
};
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
data.Add(streamContent);

data.Headers.Remove("Content-Type");
data.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
request.Content = data;
var response = await _httpClient.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
相關文章
相關標籤/搜索