攔截HttpWebResponse.GetResponse引發非200OK的錯誤碼

當我們在進行網路HTTP通訊的設計下,有一個詭異的情況會發生,當我們試圖去用System.Net.HttpWebRequest去取用遠端的某一個頁面,並在程式碼外圍包覆一層Try-Cache,事實上當某頁面回傳的HTTP代碼並非200 OK(System.Net.HttpStatusCode.OK)時,例如:GetResponse()時對方Http Header返回404 FileNotFound(System.Net.HttpStatusCode.NotFound),這時候程式碼竟然會幫你跳到Catch段(該說貼心嗎?)

程式碼長的如下:

using (System.Net.HttpWebResponse oRep = (System.Net.HttpWebResponse)oReq.GetResponse())
{
  //當遠端回傳404頁面資訊,這時候根本不會進來此處執行,而是跳到Catch段落
}

一般來說,這也不是甚麼大問題,但倘若你想要做某種程度的自定義顯示,例如顯示對方到底返回給你怎麼樣的Http Status Code,這下就慘了,因為你根本連HttpWebResponse物件都還沒取得就跳錯誤了,又怎麼能取得Http Status代碼呢?

WebException.Status無法提供充足的資訊

有些人會說,那就用WebException.Status屬性啊?問題是在上述404的情況下,他只會回給你「ProtocolError 7」的錯誤,當然你去取ErrorMessage裡面可能真的會出現Http Status的404代碼啦,不過,你確定要分析與拆解文字嗎?

不妨試試看WebException.Response

原來WebExceptionu有一個隱藏的Response的屬性,偷偷的實作返回一個抽象類別System.Net.WebResponse,把一切本應該是System.Net.HttpWebResponse接收的資料都收進去了,所以,就用System.Net.HttpWebResponse把這些資訊拿回來吧!喔對了,System.Net.HttpWebResponse繼承自System.Net.WebResponse喔。

以我們這篇文章的例子,其實我們只需要拿回Http Status狀態而已。廢話不多說,看程式碼吧:

try
{
  using (System.Net.HttpWebResponse oRep = (System.Net.HttpWebResponse)oReq.GetResponse())
  { /* 當遠端回傳404頁面資訊,這時候根本不會進來此處執行,而是跳到Catch段落 */ }
}
catch (System.Net.WebException oEx)
{ //取得HTTP錯誤代碼
  if (oEx.Response is System.Net.HttpWebResponse)
  { var eStatus = (oEx.Response as System.Net.HttpWebResponse).StatusCode; }  //拿到對方回傳給的狀態碼了
  else
  { var eStatus = System.Net.HttpStatusCode.InternalServerError; }
  //取得錯誤描述詳細資訊
  cMessage = $"錯誤資訊如下:{oEx.Status.ToString()}/{oEx.Message}";
}
catch (System.Exception oEx)
{ 
  //發生其他錯誤時
}

同場加映:

再補充一下,有時候我們很不希望GetResponse()把這種通訊協定等級的Http錯誤,當作嚴重錯誤拋到Try-Catch段,或許反而我們是希望在Catch時期把對方網頁給你的資訊再進行某一種程度的處理或回拋,這時候可能需要下列的程式碼來協助嘍。

catch (System.Net.WebException oEx)
{ //ProtocolError才可能會有HTTP回傳內容
  if (oEx.Status == System.Net.WebExceptionStatus.ProtocolError)
  { //再次確認是否為HttpWebResponse,以防萬一
    if (oEx.Response is System.Net.HttpWebResponse)
    { //拿到狀態碼
      System.Net.HttpStatusCode eStatus = (oEx.Response as System.Net.HttpWebResponse).StatusCode;
      //拿到真正回傳的內容
      using (System.IO.StreamReader oSR = new System.IO.StreamReader((oEx.Response as System.Net.HttpWebResponse).GetResponseStream(), System.Text.Encoding.UTF8))
      { string cContent = oSR.ReadToEnd(); }
    }
  }
}
System.Net.HttpWebRequest System.Net.HttpWebResponse System.Net.HttpStatusCode System.Net.WebException System.Net.WebExceptionStatus