6

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

问题是,当使用不同的 IP 地址(即私有网络与公共网络)时,urllib2公共网络上的请求成功,但对私有网络的相同请求失败,并带有502 Bad Gatewayor URLError [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我不知道的请求是否需要一些额外的配置,或者我是否需要查看一些可能丢失的网络配置(我不相信是这种情况,因为备用请求方法有效,但我肯定是错的)。

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

4

1 回答 1

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,因为它使用单引号而不是双引号。根据文档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 中无效的反斜杠转义之类的其他内容,并且任何使用限制评估(或更糟糕的是原始评估)进行解析的 JS 代码都将接受它. 而且,由于很多人因此习惯了编写糟糕的 JSON,许多浏览器的原生 JSON 解析器和许多其他语言的 JSON 库都有解决方法来允许常见错误。但你不应该依赖它。

于 2013-02-05T19:30:35.237 回答