【问题标题】:Inconsistent behavior with HTTP POST requests in PythonPython 中 HTTP POST 请求的行为不一致
【发布时间】:2013-02-06 03:45:12
【问题描述】:

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

问题是当使用不同的 IP 地址时(即私有网络与公共网络),公共网络上的 urllib2 请求成功,但对私有网络的相同请求失败并出现 502 Bad Gateway 或 @987654326 @。

我使用的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 请求是否需要一些额外的配置,或者我是否需要查看一些可能丢失的网络配置(我不相信就是这样,因为备用请求方法有效,但我肯定是错的)。

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

【问题讨论】:

  • 如果您比较两者发送的标头,它们将不完全相同。 (例如,requests 默认为Accept-Encoding: gzip, deflate, compress,而urllibAccept-Encoding: identity。)捕获每个版本的请求标头,并使用例如nc 对服务器重放它们并查看它如何响应。 urllib2 标头的某些内容导致 502 错误,或者它正在执行某种重定向/等。 urllib2 不能理解 requests 。
  • 另外...如果它与requests 一起使用,您是否有理由不能只使用requests
  • urllib2.Request 的文档表明 data 参数应该被 urlencoded 为 application/x-www-form-urlencoded
  • +1 到@AustinPhillips。看起来您可以创建一个没有数据的Request,然后使用任意字节字符串调用req.add_data(data),然后再调用urlopen。试试看它是否有效。
  • @abarnert 谢谢!我会尝试添加数据。我不想只是切换到requests 而不找出为什么我看到这个问题的原因是可能存在一些更深层次的潜在问题,这可能会再次出现并导致更难检测的问题稍后的。谢谢!

标签: python node.js http rest urllib2


【解决方案1】:

这里的问题是,正如 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才能运行它),因为每个版本的语法不同,但它总是像@这样简单987654332@.

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

【讨论】:

  • 优秀的答案。 netcat 提示非常有用,我会记住它以备将来使用。我确实将json.dumps 用于我的实际代码,但为了减少问题大小,我将其省略了。然而,这是一个非常好的观察结果,我会记住它以备将来使用。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2021-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-15
  • 2017-11-28
  • 2013-06-10
相关资源
最近更新 更多