2013-02-04 33 views
6

嘗試在Python(WSGI)和NodeJS + Express應用程序之間發出POST請求。他們在不同的服務器上。Python中的HTTP POST請求的行爲不一致

的問題是,使用不同的IP地址(即,專用網絡與公用網絡)時,在公共網絡上的urllib2請求成功,但是專用網絡相同的請求失敗,502 Bad GatewayURLError [32] Broken pipe

urllib2代碼我使用的是這樣的:

req = urllib2.Request(url, "{'some':'data'}", {'Content-Type' : 'application/json; charset=utf-8'}) 

res = urllib2.urlopen(req) 

print f.read() 

現在,我也編碼這樣的要求,使用requests

r = requests.post(url, headers = {'Content-Type' : 'application/json; charset=utf-8'}, data = "{'some':'data'}") 

print r.text 

,並獲得200 OK響應。這種替代方法適用於兩個網絡。

我有興趣瞭解是否需要某個urllib2請求的一些額外配置,或者我需要查看一些可能丟失的網絡配置(我不相信這個是這種情況,因爲替代請求方法有效,但我肯定是錯的)。

任何建議或指針與此將不勝感激。謝謝!

+1

如果比較兩者發送的標題,它們將不會相同。 (例如'request'默認爲'Accept-Encoding:gzip,deflate,compress',而'urllib'爲'Accept-Encoding:identity'。)捕獲每個版本的請求頭,並使用服務器,例如'nc',看看它是如何響應的。無論是關於'urllib2'頭文件導致502錯誤,或者它正在做一些重定向/等。 urllib2所理解的請求不會。 – abarnert

+0

另外...如果它與'requests'一起工作,是否有一個原因,你不能只使用'requests'? – abarnert

+2

['urllib2.Request'](http://docs.python.org/2/library/urllib2.html#urllib2.Request)的文檔指出* data *參數應該被urlencoded爲* application/x- WWW窗體-urlencoded *。 –

回答

3

這裏的問題是,奧斯汀·菲利普斯指出,urllib2.Request的構造的data參數:

可以是指定的附加數據發送到服務器的字符串... data應該是在標準的緩衝application/x-www-form-urlencoded格式。 urllib.urlencode()函數採用2元組的映射或序列,並以此格式返回字符串。

通過傳遞它JSON編碼的數據而不是urlencoded數據,你會混淆它的某個地方。

然而,Request有一個方法add_data

請求數據集的數據。除了HTTP處理程序外,所有處理程序都會忽略它 - 並且它應該是一個字節字符串,並將請求更改爲POST而不是GET。

如果你使用這個,你應該也使用add_header,而不是通過它在構造函數中,但似乎沒有要在文件中明確提及任何地方。

所以,這應該工作:

req = urllib2.Request(url) 
req.add_data("{'some':'data'}") 
req.add_header('Content-Type', 'application/json; charset=utf-8') 
res = urllib2.urlopen(req) 

在評論,你說:

的原因,我不希望只是切換到請求沒有查清原因我看到這個問題是,可能會有一些更深層次的根本問題,這指出可能會回來,並在以後導致難以發現的問題。

如果你想找到深層次的問題,你不會通過查看你的客戶端來源來做到這一點。確定「爲什麼X能夠工作但Y失敗?」的第一步與網絡代碼是確切地找出X和Y每個字節發送。然後,您可以嘗試縮小相關差異,然後確定代碼的哪一部分導致Y在相關位置發送錯誤的數據。

你可以通過在服務上記錄事情(如果你控制它),運行Wireshark等來做到這一點,但最簡單的方法是netcat。您需要爲您的系統閱讀man nc(並且在Windows上,您需要先安裝netcat才能運行它),因爲每個版本的語法都不相同,但它總是像nc -kl 12345這樣簡單。

然後,在您的客戶端中,將URL更改爲使用localhost:12345代替主機名,它將連接到netcat併發送其HTTP請求,該請求將被轉儲到終端。然後,您可以複製該文件並使用nc HOST 80並粘貼它以查看真實服務器如何響應,並使用它來縮小問題的位置。或者,如果您遇到問題,至少您可以將數據複製並粘貼到您的SO問題中。


最後一兩件事:這是幾乎可以肯定不相關的問題(因爲你與requests發送完全相同的數據,它的工作),但你的數據是沒有實際有效的JSON,因爲它使用單報價而不是雙引號。據the docsstring被定義爲:

string 
    "" 
    " chars " 

(該文檔有一個漂亮的圖形表示法爲好)

一般來說,除了非常簡單的測試用例,你不想寫JSON用手。在很多情況下(包括你的),你所要做的就是用json.dumps(…)替換"…",所以這不是一個嚴重的困難。所以:

req = urllib2.Request(url) 
req.add_data(json.dumps({'some':'data'})) 
req.add_header('Content-Type', 'application/json; charset=utf-8') 
res = urllib2.urlopen(req) 

那麼,爲什麼它的工作?那麼,在JavaScript中,單引號的字符串是合法的,以及其他的東西,如在JSON中無效的反斜槓轉義符,並且任何使用restricted-eval(或更糟糕的是,eval)解析的JS代碼都會接受它。而且,由於許多人習慣於編寫糟糕的JSON,因此許多瀏覽器的本機JSON解析器和其他語言中的許多JSON庫都有解決方法來允許常見錯誤。但你不應該依賴這一點。

+0

優秀的答案。 'netcat'提示非常有用,我會記住它以備將來使用。我對我的實際代碼使用了'json.dumps',但是我放棄了它來減少問題的大小。然而,這是一個非常好的觀察,我會牢記以備將來使用。非常感謝。 –