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

时间:2013-02-04 20:57:13

标签: python node.js http rest urllib2

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

问题在于,当使用不同的IP地址(即专用网络与公共网络)时,公共网络上的urllib2请求成功,但对专用网络的相同请求失败,{{1} }或502 Bad Gateway

我正在使用的URLError [32] Broken pipe代码是:

urllib2

现在,我还使用requests编译了这样的请求:

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

res = urllib2.urlopen(req)

print f.read()

获得r = requests.post(url, headers = {'Content-Type' : 'application/json; charset=utf-8'}, data = "{'some':'data'}") print r.text 回复。这种替代方法适用于两个网络。

我有兴趣了解我不知道的200 OK请求是否需要一些其他配置,或者我是否需要查看可能缺少的某些网络配置(我不知道)我认为情况就是这样,因为备用请求方法有效,但我肯定是错的。)

任何有关此建议或指示都将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:3)

问题在于,正如Austin Phillips所指出的那样,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,因为它使用单个引号而不是双引号。根据{{​​3}},string定义为:

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库都有解决方法来解决常见错误。但你不应该依赖它。