在前幾篇有提到因為專案版本限制所以使用 WebClient 來呼叫 API ,後來在測試時出現一個奇怪的問題:
某個 API 需要在 Header 增加自訂字串,傳入校驗資訊。使用 Postman 測試正常,使用 WebClient 呼叫時不加入 Header 伺服器回傳錯誤訊息,但是加上自訂 Header 後回應訊息就變成亂碼,回傳的亂碼訊息範例如下:
經過一翻研究後發現是因為加入 Header 後驗證通過,回應的內容比較多,所以目標伺服器自動使用 gzip 壓縮回應內容,而 WebClient 不像 HttpClient 可以自動解壓縮 gzip ,於是就出現亂碼了。
參考資料:
Microsoft.Learn - HttpClientHandler.AutomaticDecompression Property
stack overflow - Automatically decompress gzip response via WebClient.DownloadData
某個 API 需要在 Header 增加自訂字串,傳入校驗資訊。使用 Postman 測試正常,使用 WebClient 呼叫時不加入 Header 伺服器回傳錯誤訊息,但是加上自訂 Header 後回應訊息就變成亂碼,回傳的亂碼訊息範例如下:
}?MO?0@?K?`???:???p&&?p?k?*&Zec?S?4?@?????:??b????CC??/╗??>&&_?>?)??T?SRV?e??y8?7?oSR
?!f?J3L?lN&&?e#??T?v'??????^?╗??═??1?N?????MC????]_A???HGd? -???a?|o??|K?^ K???╔Α╝?,?1\?cY????c<??
經過一翻研究後發現是因為加入 Header 後驗證通過,回應的內容比較多,所以目標伺服器自動使用 gzip 壓縮回應內容,而 WebClient 不像 HttpClient 可以自動解壓縮 gzip ,於是就出現亂碼了。
判斷是否有 gzip 壓縮
response 的壓縮格式會記錄在 Header 中,名稱為 Content-Encoding ,所以只要檢查如果有 Content-Encoding 且等於 gzip ,就需要解壓縮。判斷壓縮的程式碼:
using (var client = new WebClient())
{
try
{
string url = "https://localhost:7041/api/user/2";
client.Encoding = Encoding.UTF8; // 不指定的話中文會是亂碼
// 發送 GET 請求並取得回應
string response = client.DownloadString(url);
Console.WriteLine(response);
// 如果有 Content-Encoding 代表經過壓縮
string clientResponseHeader = client.ResponseHeaders["Content-Encoding"];
Console.WriteLine("Content-Encoding: " + clientResponseHeader);
}
catch (WebException e)
{
Console.WriteLine("API 錯誤: " + e.Message);
}
}
gzip 解壓縮
在 WebClient 中取回的內容是 byte[] ,把它解壓縮並轉換成字串的程式碼如下:
public static string FromGZipBytes(byte[] input)
{
using (var decompressedStream = new MemoryStream(input))
using (var gzipStream = new GZipStream(decompressedStream, CompressionMode.Decompress))
using (var reader = new StreamReader(gzipStream))
{
return reader.ReadToEnd();
}
}
完整程式碼
public static void Get()
{
using (var client = new WebClient())
{
try
{
const string url = "http://localhost:5000/User/1";
string body = "body";
client.Encoding = Encoding.UTF8;
client.Headers.Add("Content-Type", "application/json");
client.Headers.Add("MyHeader", "MyHeader");
var bytesResult = client.UploadData(url, "POST", Encoding.UTF8.GetBytes(body));
string clientResponseHeader = client.ResponseHeaders["Content-Encoding"];
string result = "";
if (clientResponseHeader == "gzip")
{
result = FromGZipBytes(bytesResult);
}
else
{
result = Encoding.UTF8.GetString(bytesResult);
}
Console.WriteLine(result);
}
catch (Exception e)
{
Console.WriteLine("API 錯誤: " + e);
throw;
}
}
}
public static string FromGZipBytes(byte[] input)
{
using (var decompressedStream = new MemoryStream(input))
using (var gzipStream = new GZipStream(decompressedStream, CompressionMode.Decompress))
using (var reader = new StreamReader(gzipStream))
{
return reader.ReadToEnd();
}
}
參考資料:
Microsoft.Learn - HttpClientHandler.AutomaticDecompression Property
stack overflow - Automatically decompress gzip response via WebClient.DownloadData
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com