

【茶包射手日記】PowerShell Invoke-WebRequest 呼叫 IIS 出錯時顯示亂碼
source link: https://blog.darkthread.net/blog/ps-invoke-webrequest-error-encoding/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【茶包射手日記】PowerShell Invoke-WebRequest 呼叫 IIS 出錯時顯示亂碼-黑暗執行緒
同事反映 WebAPI 有問題,我提供了一段 PowerShell Invoke-WebRequest -Method POST 程式片段請他對照錯誤,他回報出現 502 錯誤,但訊息是亂碼:(為方便觀察及說明,以下用 HTTP GET 500 錯誤代替)

觀察錯誤回傳結果,找到原因:IIS 所在作業系統是中文版 Windows,出錯時吐回的自訂錯誤畫面,採用的還是古老的 charset=big5 編碼。

由此推測 PowerShell 在出錯時,試圖解析 HTTP 錯誤網頁的 HTML 將其轉換為純文字,但錯在未能正確識別成 BIG5 編碼,錯用 UTF-8 解析,才噴出亂碼。用一小段 C# 模擬用 UTF-8 硬解 BIG5 編碼的「伺服器錯誤 500 - 內部伺服器錯誤。 您要尋找的資源有問題而無法顯示。」字串,得到完全相同的結果,可得證。
猜想應是網站吐回的內容未指定 CharSet,PowerShell 應會採用預設編碼解析,我如果能將預設編碼暫時改為 Big5,問題就有解了。找不到有文件提及如何修改 Invoke-WebRequest 解析 HTML 時的預設語系編碼,決定從原始碼找答案。我查到當 HTTP 狀態碼失敗時的解析邏輯,是透過 WebResponseHelper.GetCharacterSet(response) 取得 CharSet,作為後續 StreamHelper.DecodeStream 時的參數。
if (ShouldCheckHttpStatus && !_isSuccess)
{
string message = string.Format(
CultureInfo.CurrentCulture,
WebCmdletStrings.ResponseStatusCodeFailure,
(int)response.StatusCode,
response.ReasonPhrase);
HttpResponseException httpEx = new(message, response);
ErrorRecord er = new(httpEx, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request);
string detailMsg = string.Empty;
try
{
// We can't use ReadAsStringAsync because it doesn't have per read timeouts
TimeSpan perReadTimeout = ConvertTimeoutSecondsToTimeSpan(OperationTimeoutSeconds);
string characterSet = WebResponseHelper.GetCharacterSet(response);
var responseStream = StreamHelper.GetResponseStream(response, _cancelToken.Token);
int initialCapacity = (int)Math.Min(contentLength ?? StreamHelper.DefaultReadBuffer, StreamHelper.DefaultReadBuffer);
var bufferedStream = new WebResponseContentMemoryStream(responseStream, initialCapacity, this, contentLength, perReadTimeout, _cancelToken.Token);
string error = StreamHelper.DecodeStream(bufferedStream, characterSet, out Encoding encoding, perReadTimeout, _cancelToken.Token);
detailMsg = FormatErrorMessage(error, contentType);
}
catch (Exception ex)
{
// Catch all
er.ErrorDetails = new ErrorDetails(ex.ToString());
}
if (!string.IsNullOrEmpty(detailMsg))
{
er.ErrorDetails = new ErrorDetails(detailMsg);
}
ThrowTerminatingError(er);
}
追進去,WebResponseHelper.GetCharsetSet()由 response.Content.Headers.ContentType?.CharSet
取得 CharSet (可能是 null),
檢查 IIS 出錯時回傳自訂錯誤頁面的 Response Header,Content-Type 只有 text/html 無 CharSet,故 PowerShell 會抓到 null。
當 CharSet 作為參數傳入 StreamHelper.DecodeStream() 解析 HttpResponseStream,CharSet 缺值或不對時會改用 ContentHelper.GetDefaultEncoding() 為準,該方法寫死 internal static Encoding GetDefaultEncoding() => Encoding.UTF8;
沒得改,宣告此路不通。
題外話,Github 現在認得 C#,能簡單識別類別、方法,列出有參考到它的地方,還蠻好用的。
出不轉路轉,如果你有 Cmder、Git Bash 或 Windows 10/11 (見文末更新),可以改用 curl 存成 HTML 再看:
如果吞不下這口氣,或測試機器上沒有 curl 等工具,一定要用 PowerShell 讀出結果,可以 catch WebException 再用 StreamReader 指定 BIG5 編碼開啟 ResonseStream,即能正確讀出內容。
try {
Invoke-WebRequest -Uri http://192.168.50.3
}
catch [System.Net.WebException] {
$stm = $_.Exception.Response.GetResponseStream()
$stm.Position = 0
$big5Text = (New-Object System.IO.StreamReader($stm, [System.Text.Encoding]::GetEncoding(950))).ReadToEnd()
$html = New-Object -Com 'HTMLFile'
$html.IHTMLDocument2_write($big5Text)
$html.body.InnerText.Split([System.Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
}
2023-07-21 更新
2017/11/19 起 Windows 10/11 已經內建 curl 了 (Windows Server 沒有) 參考,因為在 PowerShell curl 被導向 Invoke-WebRequest,我一直沒發現。感謝讀者 Kuo Chin-Yuan 補充。
Recommend
-
10
Invoke-WebRequest response empty when calling Azure function URL using Powershell....
-
17
【茶包射手日記】怪異的 .NET 數學運算溢位錯誤-黑暗執行緒 同事報案,某支提供檔案下載的 ASP.NET 程式,用瀏覽器可正常開啟,但有一支用 HttpWebRequest 存取它的 .NET 程式,原本可以執行,現在則出現 Arithmetic operation resulted in an ove...
-
11
【茶包射手日記】IIS 整合式登入 MSSQL 之 AppPool 身分疑雲-黑暗執行緒前天提到我在 IIS 試跑 ASP.NET Core 開源專案 - Kandu,它的資料庫是用 MSSQL,本機跑測試我懶得開帳號,便把連線字串設成 Integrated Security=SSPI 打算走整合式驗證,用 AppPool 的身...
-
12
【茶包射手日記】IIS 跑 ASP.NET Core 出現 500.19 0x8007000d 錯誤-黑暗執行緒 低級錯誤一枚。 我玩 ASP.NET Core 多半是跑 VS 偵錯後丟進 Docker 跑,很久沒部署到 IIS,但方法我還記得:Publish、開 AppPool,也記得要設成「沒有受控碼」(No Man...
-
13
【茶包射手日記】從 Oracle 改用 MSSQL 出現中文亂碼-黑暗執行緒 將某個老系統的資料來源由 Oracle 換成 MSSQL,遇到一個有意思的狀況。 在 Oracle 與 MSSQL 都有 Schema 完全相同的資料表,資料原本來自 Oracle NVARCHAR,改用 MSSQL 後,部分中文...
-
13
【茶包射手日記】偶發型 Oracle Client 版本不相容錯誤-黑暗執行緒同事報案,有個 ASP.NET 網站「偶爾」會出現提供者與 Oracle 從屬版本不相容錯誤。這是 Unmanaged ODP.NET...
-
23
Invoke-WebRequest 存取具 Session 狀態網頁-黑暗執行緒在 PowerShell 要爬網站或存取網頁,Invoke-WebRequest 是不二選擇。有時我們會存取具有 Session 狀態的網頁,例如:要先登入...
-
23
【茶包射手日記】PowerShell 串接 EXE 輸出中文變亂碼-黑暗執行緒上週介紹過用 PowerShell 串接 EXE 輸出結果的技巧,後續遇到一個問題 - 有些 EXE 程式輸出的中文會變亂碼...
-
5
【茶包射手日記】Invoke-WebRequest 特定網頁卡住無回應-黑暗執行緒 今天踩到的雷,PowerShell Invoke-WebRequest 讀取某個網頁會當掉沒反應,必須按 Ctrl-C 才能脫逃,但如果寫 (Invoke-WebRequest -Uri ...).Content 卻可以馬上取回結果。 被卡住...
-
6
【茶包射手筆記】PowerShell Invoke-WebRequest IE 錯誤-黑暗執行緒 某段 Invoke-WebRequest 指令開發測試完,部署到目的主機,經手動執行確認 OK,設成排程卻出現以下錯誤訊息: The response content cannot be parsed because th...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK