与Python中的HTTP POST请求不一致的行为

尝试在Python(WSGI)和NodeJS + Express应用程序之间发出POST请求。 他们在不同的服务器上。

问题是,当使用不同的IP地址(即专用networking与公用networking)时,公共networking上的urllib2请求成功,但是对于专用networking的相同请求却失败,出现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() 

现在,我也使用request来编写这样的requests

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

得到一个200 OK回应。 这种替代方法适用于这两个networking。

我有兴趣了解是否有一些额外的configuration需要我不知道的urllib2请求,或者如果我需要查看一些可能会丢失的networkingconfiguration(我不相信这是事实,因为替代请求方法起作用,但我肯定是错的)。

任何build议或指针与此将不胜感激。 谢谢!

这里的问题是,正如Austin Phillips指出的, urllib2.Request的构造函数的data参数:

可能是指定要发送到服务器的附加数据的string… data应该是标准应用程序/ x-www-form-urlencoded格式的缓冲区。 urllib.urlencode()函数采用2元组的映射或序列,并以此格式返回string。

通过传递它JSON编码的数据而不是urlencoded数据,你在某处混淆了它。

但是, Request有一个方法add_data

将请求数据设置为数据。 除了HTTP处理程序之外,所有的处理程序都会忽略它 – 在那里它应该是一个字节string,并且会将请求更改为POST而不是GET。

如果你使用这个,你可能也应该使用add_header而不是在构造函数中传递它,尽pipe在文档的任何地方都没有提到它。

所以,这应该工作:

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

在一个评论中,你说:

我不希望只是转而请求,却不知道为什么我会看到这个问题,可能会有一些更深层次的根本性问题,这些问题可能会回来,并在以后导致难以发现的问题。

如果你想find深层次的问题,你不会仅仅通过查看你的客户端来源来做到这一点。 找出“为什么X能工作但Y失败?”的第一步。 与networking代码是确切地找出什么字节X和Y每个发送。 然后你可以尝试缩小相关的区别,然后找出你的代码的哪一部分导致Y在相关位置发送错误的数据。

你可以通过在服务上logging事情(如果你控制它),运行Wireshark等等,但最简单的方法是netcat。 你需要为你的系统阅读man nc (在Windows上,你需要先安装netcat,然后才能运行它),因为每个版本的语法都不一样,但是它总是像nc -kl 12345一样简单nc -kl 12345

然后,在你的客户端中,将URL改为使用localhost:12345代替主机名,它将连接到netcat并发送它的HTTP请求,并将其转储到terminal。 然后,您可以复制该文件并使用nc HOST 80并将其粘贴,以查看真实服务器如何响应,并使用它来缩小问题的位置。 或者,如果您遇到困难,至less您可以将数据复制并粘贴到您的SO问题中。


最后一件事:这几乎肯定与您的问题无关(因为您发送的requests数据完全相同,而且正在工作),但是您的数据实际上并不是有效的JSON,因为它使用单引号而不是双引号。 根据文档 , string被定义为:

 string "" " chars " 

(文档也有很好的graphics表示。)

一般来说,除了非常简单的testing用例外,您不需要手动编写JSON。 在很多情况下(包括你的),你所要做的就是用json.dumps(…)replace"…" ,所以这不是一个严重的困难。 所以:

 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中,单引号的string是合法的,以及其他的东西,如在JSON中无效的反斜杠转义,并且任何使用restricted-eval(或更糟糕的是,eval)parsing的JS代码都会接受它。 而且,由于许多人习惯于编写不良的JSON,因此许多浏览器的本地JSONparsing器和其他语言的许多JSON库都有解决方法,可以解决常见的错误。 但是你不应该依赖这个。