一直使用Delphi寫程序,因為習慣了,用起來方便。
但是有一個問題困擾了我半年了。就是使用Idhttp Post提交時候總會有莫名其妙的錯誤,大部分網站沒問題,但是一遇到Asp.net就報錯500。
想了很多辦法,找了很多資料,都沒有一個能正確解決我問題的。甚至有人提到了問題的解決綱要,但是最多都只是靠邊,沒有實際解決掉。
後來無奈,開始使用RTC的HTTP控件,但是RTC沒有完善的Cookie管理機制,我費盡力氣使用IdCookieManager去作為管理器管理他,結果還是有問題,對於一個常規Cookies如下:
Set-Cookie: loginInfo=aabbcc; domain=aaaa.com; expires=Tue, 16-Jul-2013 09:00:20 GMT; path=/;httponly
RTC總是會給他拆分成
Set-Cookie: loginInfo=-Cookie: domain=aaaa.-Cookie: expires=Tue, 16-Jul-2013 09:00:20-Cookie: path=/;
這很讓我莫名其妙,我使用嗅探探測是上面的,但是獲取Response.headertext的時候,裡面就變成了下面這樣。。
第一次遇到空間會直接強制修改頭的。。。
但是這還不是最讓我郁悶的,當我不管三七二十一,將cookie發送出去時候,CookieText為:
Cookie:loginInfo=aabbcc;kakaka=aaaaa;bbbb=ccccc;
當發送時變成了這樣:
Cookie:loginInfo=aabbcc; Cookie:kakaka=aaaaa; Cookie:bbbb=ccccc;
這下徹底讓我崩潰了,這簡直是胡扯麼,這樣服務端根本無法正確識別的。
可能是對RTC不熟悉吧,如果有人知道解決方法也告知我下。
下面是重點:
因此我不得不重新開始研究Delphi,首先是解決頭文件Accept-Encoding總是附加"identity"的問題,對DelphiXE2的Indy控件進行了重新編譯,改用了Indy10_5022。
修改了idhttp.pas裡
IndyPos(, ARequest.AcceptEncoding) = ARequest.AcceptEncoding <> ////ARequest.AcceptEncoding := ARequest.AcceptEncoding + ; = ; ;
的強制附加代碼,當我指定AcceptEncoding時,則按照我指定的來,不進行附加。
然後發現問題依舊。
對錯誤進行了過濾,然後獲取IDHTTP.URL信息,發現地址不是我之前訪問的login.aspx,而變成了index.aspx。
一開始我以為是我地址輸入錯了,但是找遍源代碼也沒發現index.aspx的地方。
我開始嗅探,在協議裡發現了302跳轉,於是想起了以前對這個的研究有發現的這個的問題,也是別人一個帖子裡提到的,post->a頁面時候,因為302跳轉,則繼續post給了下一個跳轉的頁面。
分析嗅探信息,發現的確如此:
POST /User/index.aspx HTTP/1.1
而接收到的信息是這樣的:
HTTP/1.1 500 Internal Server Error Date: Tue, 16 Jul 2013 08:08:34 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50727 Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 7868
這個是post給 index.aspx的返回信息,500錯誤。HTTP內容意思是非法提交導致asp.net的錯誤。
asp.net有嚴格的表單驗證,如果進行post提交時帶有的驗證表單參數和地址等信息不符合時,則會報錯。
那我就明白了,正確處理方法應該是:POST->302->GET正確地址。
我嘗試以下代碼:
=IdHTTP1.Post( =IdHTTP1.Response.RawHeaders.Values[ IdHTTP1.Response.RawHeaders.Values[][]= =IdHTTP1.Response.RawHeaders.Values[=== Pos(,IdHTTP1.Response.RawHeaders.Values[])> =IdHTTP1.Response.RawHeaders.Values[ pos(,IdHTTP1.Response.RawHeaders.Values[])= =IdHTTP1.Response.RawHeaders.Values[==
證明了我的想法是正確的,但是難道每次我都必須要這樣一長串代碼和不安全的跳轉處理去做跳轉麼?
這顯然不安全,如果出現我沒考慮到的規則,那麼就會有錯誤。我考慮看看Idhttp源代碼裡如何處理的,想做一下修改。
結果在源代碼裡發現了這個:
((LResponseCode = ) (hoTreat302Like303 FHTTP.HTTPOptions)) = ) = = =;
哈哈,恍然大悟,我對代碼做了一下優化:
IdHTTP1.HandleRedirects:=True; IdHTTP1.ProtocolVersion:=pv1_1; IdHTTP1.HTTPOptions:=IdHTTP1.HTTPOptions+[hoTreat302Like303]; Memo1.Text:=IdHTTP1.Post('http://www.*.com/User/login.aspx',StrLst,IndyTextEncoding(encUTF8));
運行,一切正常,獲取到了正確信息。
不過為什麼會這樣呢?根據302like303的意思是,當遇到302錯誤時按照303進行執行。
百度了下資料如下:
302 作為HTTP1.0的標准,以前叫做Moved Temporarily ,現在叫Found.
現在使用只是為了兼容性的處理,包括PHP的默認Location重定向用的也是302.
但是HTTP 1.1 有303
和307作為詳細的補充,其實是對302的細化
303:對於POST請求,它表示請求已經被處理,客戶端可以接著使用GET方法去請求Location裡的URI。
這下明白了,其實實際應該是寫代碼的人沒有正確使用跳轉,大多數人直接將302當作303在用。302會繼承Method,而303則是再次使用GET。
至此,困擾了我半年的問題終於解決。這半年來我一直無法處理Asp.net的站點。其原因竟然只是因為一個參數。。。。
因為我沒有找到網上對這個問題的處理方法,寫出這篇文章給後來人,以免造成像我這樣的杯具。