2013-02-04 32 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',但是我放弃了它来减少问题的大小。然而,这是一个非常好的观察,我会牢记以备将来使用。非常感谢。 –