網頁

以 requestb.in 解開 HTTP Request 之密

以 requestb.in 解開 HTTP Request 之密

公司有個特別的 D 系統,我們需要透過 ASP.NET Web API 去存取它的 XML Web Service 來提供資源。這個 D 系統本身有個特別的限制,就是存取之前使用者需要先進行頁面 Login,然後才能存取 XML Web Service。Login 頁面很單純,就是一個帳號與密碼的組合,沒有其他特別驗證碼等保護。也就是使用者使用者輸入帳號密碼,而 ASP.NET Web API 透過使用者提供的帳號密碼透過程式方式進行登入,我們開發的 ASP.NET Web API 服務從一開始的 Beta 至 RC,這部分的程式碼都沒什麼問題,直到那令人鬼打牆的關鍵人物(暫稱他苦命的呆伯特好了)出現。

呆伯特一直說無法Login

簡單說明一下,此 ASP.NET Web API 服務主要使用者在美國,美國下班台灣上班的黃金交接點,我們私下請呆伯特(美國)幫忙測試此 ASP.NET Web API 服務的同事,但幾星期以來僅呆伯特都一直反應無法 Login 服務,由日誌看出來,他確實是登入失敗。但所有幫忙測試的人員只有呆伯特會出現此狀況。D 系統我們無任何權限,我們能做的也只是不斷調整 ASP.NET Web API 程式並不斷請呆伯特幫忙 Login 與測試。但每每得到"不行"時,心情都低落到不行(測到呆伯特都生氣了),不過最後得到一條重要資訊,呆伯特的密碼含有數個的特殊符號

我們為加解密演算法補上特殊符號的測試程式碼,先確保特殊符號在加密與解密過程正常。其中小心\"這兩個符號,在C#的字串中,需要使用\\\"進行轉義。

以這條線索測試到最後終於有了曙光。

.NET WebRequest - Web Login 程式

public void useNetWebRequest(string url, string name, string pw)
{
 CookieContainer cookies = new CookieContainer();
 var request = (HttpWebRequest)WebRequest.Create(url);
 request.CookieContainer = cookies;
 request.Method = "POST";
 request.ContentType = "application/x-www-form-urlencoded";
 using (var requestStream = request.GetRequestStream())
 using (var writer = new StreamWriter(requestStream))
 {
  writer.Write("Username={0}&Password={1}", name, pw);
 }

 using (var responseStream = request.GetResponse().GetResponseStream())
 using (var reader = new StreamReader(responseStream))
 {
  var result = reader.ReadToEnd();
  result.Dump();
 }
} 
 

這是我用 LINQPad 覆寫的測試程式碼。它使用我的帳號密碼能正常進行登入,當我將密碼改為數個特殊符號時,確實會出錯。至此,才確定問題點(追到快哭了 >_<)。但 D 系統看得到碰不到,無法得知 D 系統在接收特殊符號到底發生了什麼事?

一整個低潮中...

requestb.in

http://requestb.in 是一個接收 HTTP 請求的服務,服務內容很簡單,就是把接收到的 HTTP 請求內容呈現出來,我通常使用在 ASP.NET Web API 去串接其他服務時,當你需要確認發出的 HTTP 請求內容是否正確時,那麼就可以使用 requestb.in 來查看。

useNetWebRequest

這是 useNetWebRequest 打出去的結果,看完一整個抓頭皮呀,可以清楚看到特殊符號的傳遞並無問題。

看著上面的程式碼與結果圖,我苦思許多,那是一段使用 WebRequest 類別程式碼,WebRequest 是相當古早的用法,如果改用現在常用的 Framework 或 API 會是一樣的結果嗎?

http://restsharp.org/ - Web Login 程式

看得到的路都被擋死,只好自己開路。

public void useRestSharp(string url, string name, string pw)
{
 CookieContainer cookies = new CookieContainer();
 var client = new RestClient(url);
 client.CookieContainer = cookies;
 var request = new RestRequest("", Method.POST);
 request.AddHeader("content-type", "application/x-www-form-urlencoded");
 request.AddParameter("Username", name);
 request.AddParameter("Password", pw);
 var response = client.Execute(request);
 var result = response.Content;
 result.Dump();
}
 

RestSharp 是我個人蠻喜歡使用的一套 REST 呼叫用 API,比起 ASP.NET Web API 時期重新包裝的 HttpClient,RestSharp 語義非常清楚、直接與簡潔(等一下有 HttpClient版,你可以看看)。

useRestSharpe

RestSarp 在 RAW BODY 有個明顯的差異,特殊符號都經過 Ascii 編碼,快快上傳到測試區,老天爺呀,呆伯特不用在呆下去了。

這程式碼解決了我幾週來看的困擾。原來是 WebRequest 版程式原汁原味的傳遞特殊符號,猜測,D 系統的 Login 無法正確處理未編碼符號所造成。

System.Net.Http.HttpClient - Web Login 程式

public void useHttpClient(string url, string name, string pw)
{
 var cookie = new CookieContainer();
 using (var handler = new HttpClientHandler() { CookieContainer = cookie })
 using (var client = new HttpClient(handler) { BaseAddress =  new Uri(url) })
 {
  var content = new FormUrlEncodedContent(new[]
   {
    new KeyValuePair("Username", name),
    new KeyValuePair("Password", pw)
   });
  var result = client.PostAsync(url, content).Result;
  result.EnsureSuccessStatusCode();
  var resultContent = result.Content.ReadAsStringAsync().Result;
  cookie.Dump();
  resultContent.Dump();
 }
}  
 

HttpClient 打出來的結果與 RestSharp 一致,符號都是編碼過的。

小結

新版的 RESTful Framework API 在處理符號這件事情上都有考慮周全。也順便透過這個案例為大家介紹一下 requestb.in 這個服務。最後小小的注意一下,requestb.in 服務如果一開始的 Private 未勾選,那麼它會是公開狀態,也就是任何人都能看到測試結果,請不要把正式資料打上去測試哦。或是可以參考他們另一個付費專案 https://www.runscope.com/

希望呆伯特不要以為我們在搞他,我們是很認真在抓蟲,還好蟲不在 D 系統,不然,就可以請拳四郎打我一拳。也感謝有呆伯特的出現,才能在上線測試前就找出這前細小差異造成的錯誤。

1 則留言:

  1. HttpUtility.ParseQueryString 產生的 NameValueCollection,是所有人的好朋友...

    回覆刪除

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。