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


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

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

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

1)基於 WebRequest 的實現

2)基於 HttpClient 的實現

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

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

我們基於 HttpClient 的實現用的是 MultipartFormDataContent :

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" 。

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

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 也沒關系,問題與這個無關)。

繼續仔細對比抓包,發現 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();


免責聲明!

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



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