【问题标题】:python requests.put() fails when urllib3 http.request('PUT', ...) succeeds. What gives?当 urllib3 http.request('PUT', ...) 成功时,python requests.put() 失败。是什么赋予了?
【发布时间】:2017-08-18 09:12:46
【问题描述】:

我正在尝试使用 python 请求访问 Atlassian Confluence REST API。

我已经成功调用了一个GET api,但是当我调用PUT更新一个confluence页面时,它返回200,但没有更新页面。

我使用 chrome::YARC 来验证 API 是否正常工作(确实如此)。尝试调试了一段时间后,我恢复尝试使用 urllib3,效果很好。

我真的很想使用请求,但经过数小时的调试、Google 等尝试,我终生无法弄清楚这一点。

我正在运行 Mac/Python3:

$ uname -a
Darwin mylaptop.local 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64
$ python3 --version
Python 3.6.1

这是我的代码,显示了我正在尝试的所有三种方式(两个请求和一个 urllib3):

def update(self, spaceKey, pageTitle, newContent, contentType='storage'):
    if contentType not in ('storage', 'wiki', 'plain'):
        raise ValueError("Invalid contentType={}".format(contentType))

    # Get current page info
    self._refreshPage(spaceKey, pageTitle) # I retrieve it before I update it.
    orig_version = self.version

    # Content already same as requested content. Do nothing
    if self.wiki == newContent:
        return

    data_dict = {
        'type' : 'page',
        'version' : {'number' : self.version + 1},
        'body'  : {
            contentType : {
                'representation' : contentType,
                'value' : str(newContent)
            }
        }
    }
    data_json = json.dumps(data_dict).encode('utf-8')

    put = 'urllib3' #for now until I figure out why requests.put() doesn't work
    enable_http_logging()
    if put == 'requests':
        r = self._cs.api.content(self.id).PUT(json=data_dict)
        r.raise_for_status()
    elif put == 'urllib3':
        urllib3.disable_warnings() # I know, you can quit your whining now!!!
        headers = { 'Content-Type' : 'application/json;charset=utf-8' }
        auth_header = urllib3.util.make_headers(basic_auth=":".join(self._cs.session.auth))
        headers = {**headers, **auth_header}
        http = urllib3.PoolManager()
        r = http.request('PUT', str(self._cs.api.content(self.id)), body=data_json, headers=headers)
    else:
        raise ValueError("Huh? Unknown put type: {}".format(put))
    enable_http_logging(False)

    # Verify page was updated
    self._refreshPage(spaceKey, pageTitle) # Check for changes
    if self.version != orig_version + 1:
        raise RuntimeError("Page not updated. Still at version {}".format(self.version))
    if self.wiki != newContent:
        raise RuntimeError("Page version updated, but not content.")

任何帮助都会很棒。

更新 1:添加请求转储

-----------START-----------
PUT http://confluence.myco.com/rest/api/content/101904815
User-Agent: python-requests/2.18.4
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 141
Content-Type: application/json
Authorization: Basic <auth-token-here>==

b'{"type": "page", "version": {"number": 17}, "body": {"storage": {"representation": "storage", "value": "new body here version  version 17"}}}'

【问题讨论】:

  • 我以前用过这个人的例子,效果很好 - community.atlassian.com/t5/Answers-Developer-Questions/…。如果他做了不同的事情,你可以环顾四周。
  • 啊...不要隐藏您的 data... 当使用 data=json= 时,给它 dict 对象 - 不是 JSON str 代表...
  • 谢谢@droravr。这实际上是我用作脚本的起点。只是为了确保,我只是从您提供的链接中获取了他的原始代码并再次运行它。我对他的代码也有同样的问题。
  • @davfive requests.put(url, auth=self.m_auth, json=data) data 是您的数据字典而不是 JSON 字符串有效吗?
  • 请不要编辑问题以将其标记为“已解决”。发布实际答案并将其标记为已接受。

标签: python python-requests confluence-rest-api


【解决方案1】:

请求从未返回 PUT(错误???)

您观察到的是 requests 与 Web 浏览器的行为一致:使用 GET 请求对 HTTP 302 重定向做出反应。

From Wikipedia:

带有此代码的响应邀请用户代理(例如网络浏览器)向位置字段中指定的新 URL 发出第二个(其他方面相同的)请求。

(...)

许多网络浏览器以违反此标准的方式实现此代码,将新请求的请求类型更改为 GET,而不管原始请求中使用的类型(例如 POST)

(...)

因此,RFC 2616 的更新更改了定义以允许用户代理将 POST 重写为 GET。

所以这种行为与 RFC 2616 一致。我认为我们不能说这两个库中的哪个行为“更正确”。

【讨论】:

  • 不错。谢谢。我已将我的“已解决”部分从主要问题移至答案,并将您的问题标记为答案。谢谢!!
【解决方案2】:

requests 和 urllib3 模块处理从 http 到 https 切换的方式看起来有所不同。 (参见上面的@Kos 答案)。这是我检查调试日志时发现的。

所以在@JonClements 建议我向他发送响应转储后,我开始思考。在做了一些研究之后,我发现了可以调试请求和 urllib3 的神奇运行(参见 here)。

在查看两者的差异时,我注意到它们被从 http 重定向到 https 以用于我公司的 confluence 站点:

urllib3:

DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): confluence.myco.com
DEBUG:urllib3.connectionpool:http://confluence.myco.com:80 "PUT /rest/api/content/101906196 HTTP/1.1" 302 237
DEBUG:urllib3.util.retry:Incremented Retry for (url='http://confluence.myco.com/rest/api/content/101906196'): Retry(total=2, connect=None, read=None, redirect=None, status=None)
INFO:urllib3.poolmanager:Redirecting 
    http://confluence.myco.com/rest/api/content/101906196 -> 
    https://confluence.myco.com/rest/api/content/101906196
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): confluence.myco.com
DEBUG:urllib3.connectionpool:https://confluence.myco.com:443 "PUT /rest/api/content/101906196 HTTP/1.1" 200 None

虽然请求尝试使用我的 PUT,然后重定向后转到 GET:

DEBUG:urllib3.connectionpool:http://confluence.myco.com:80 "PUT /rest/api/content/101906196 HTTP/1.1" 302 237
DEBUG:urllib3.connectionpool:https://confluence.myco.com:443 "GET /rest/api/content/101906196 HTTP/1.1" 200 None

请求从未返回到 PUT

我将我的初始网址从 http: 更改为 https: 并且一切正常。

【讨论】:

    猜你喜欢
    • 2012-04-06
    • 1970-01-01
    • 2018-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 2013-01-25
    相关资源
    最近更新 更多