Python Requests中的POST请求却变成了GET请求?

32
我使用的是Python 2.7.5、Django 1.7和requests 2.4.1,正在进行一些简单的测试。但是,当我调用requests.post时,该方法似乎会执行GET操作。
我的代码与RESTful API通信。请注意,使用此有效载荷和端点,POST命令可通过Hurl.it正常工作:
def add_dummy_objective(self):
    """
    To the bank
    """
    payload = {
        'displayName': {
            'text': self._test_objective
        },
        'description': {
            'text': 'For testing of API Middleman'
        },
        'genusTypeId': 'DEFAULT'
    }
    obj_url = self.host + self.bank_id + '/objectives/?proxyname=' + self._admin_key
    req = requests.post(obj_url, data=json.dumps(payload), headers=self.headers)
    return req.json()

我正在将头部设置为 JSON:

self.headers = {
    'Content-Type'  : 'application/json'
}

与预期的POST不同,我获得了一份目标列表(这是我从GET中期望得到的)。使用pdb,我看到:

(Pdb) req.request
<PreparedRequest [GET]>
(Pdb) req.request.method
'GET'

这是怎么翻转的?我之前使用Python requests库没有问题,所以我不确定是否漏掉了什么明显的东西或者(在Django / Requests新版本中)需要设置另一个参数?这是缓存问题吗?有没有调试技巧?我尝试重新安装requests,并将Django回滚到1.6.5版本,但没有任何作用…一定很简单。--谢谢!
====== 更新1 ========
这里只是汇总Martijn在这里建议的一些调试信息:
(Pdb) requests.post.__name__
'post'

进入requests/api.py中的post()定义:

(Pdb) l
 88         :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
 89         :param \*\*kwargs: Optional arguments that ``request`` takes.
 90         """
 91         import pdb
 92         pdb.set_trace()
 93  ->     return request('post', url, data=data, **kwargs)

深入研究request()方法:
(Pdb) method
'post'
(Pdb) l
 43           >>> req = requests.request('GET', 'http://httpbin.org/get')
 44           <Response [200]>
 45         """
 46         import pdb
 47         pdb.set_trace()
 48  ->     session = sessions.Session()
 49         return session.request(method=method, url=url, **kwargs)

在session.request中再加一层:

(424)request()
-> method = builtin_str(method)
(Pdb) method
'post'
(Pdb) l
419             :param cert: (optional) if String, path to ssl client cert file (.pem).
420                 If Tuple, ('cert', 'key') pair.
421             """
422             import pdb
423             pdb.set_trace()
424  ->         method = builtin_str(method)
425
426             # Create the Request.
427             req = Request(
428                 method = method.upper(),
429                 url = url,

在方法的最后一步,请求实际被发起时,我的“prep”是POST请求,但我的响应却是GET请求:

(Pdb) prep
<PreparedRequest [POST]>
(Pdb) n
-> return resp
(Pdb) resp
<Response [200]>
(Pdb) resp.request
<PreparedRequest [GET]>
(Pdb) l
449                 'allow_redirects': allow_redirects,
450             }
451             send_kwargs.update(settings)
452             resp = self.send(prep, **send_kwargs)
453
454  ->         return resp
455
456         def get(self, url, **kwargs):
457             """Sends a GET request. Returns :class:`Response` object.
458
459             :param url: URL for the new :class:`Request` object.

你发的代码不会产生GET请求,而是会产生POST请求。你确定自己执行的是正确的代码路径吗? - Martijn Pieters
这是我的单元测试的一部分,我正在逐行执行它,所以我99%确定它遵循的是这条代码路径--如果有其他检查方法可以消除那1%的不确定性,我不知道如何做,但很乐意去尝试... - user
顺便说一句,我同意并理解上述代码应该产生一个POST...所以我认为其他地方出了问题(1%的可能性)。如果我正在使用错误的代码路径,我还能用什么其他方法找到我正在执行的路径(除了pdb)? - user
也许在别处的代码中执行了 requests.post = requests.getrequests.post.__name__ 会产生什么结果? - Martijn Pieters
2
进入requests.post调用,使用l检查周围的上下文,进一步深入并查看发送请求所使用的方法等。 - Martijn Pieters
谢谢Martijn,我会尝试一些这些方法并在问题中发布结果。requests.post.name 返回'post'... - user
2个回答

53

需要明确的是,每当请求收到一个重定向(具有某个状态码),我们必须对请求执行某些转换。

在这种情况下,当您看到一些非常意外的东西时,最好的调试提示是使用allow_redirects=False重试请求。这将立即返回30x响应。 或者,您还可以检查r.history以查看是否有任何已经跟随的30x响应。在这种情况下,您可能会看到类似于以下内容:

>>> r.request.method
'GET'
>>> r.history
[<Response [302]>,]
>>> r.history[0].request.method
'POST'

我们知道这样做可能会对用户造成意外行为(就像刚才发生的那样),但这是在网络上操作的唯一正确方式。

我希望这可以帮助您理解 为什么 会发生这种情况,超出了重定向的事实,并且希望它能为您和其他人提供未来调试的工具。


感谢您清晰的解释!在找到这个之后,我阅读了一些关于重定向的内容,并更好地理解了库的行为方式... - user
对于任何未来的读者,allow_redirects=False检查然后打印出所有内容非常有效地诊断可能导致重定向发生的原因,因为如果在重定向发生后检查信息,它将是不同的。 - Ethan Brouwer

29
问题是RESTful API将我从http://重定向到https://,这导致库返回了“第二个”请求(GET)...

这正是我的问题。 - Liza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接