模拟HTTP POST请求产生意外结果
---问题解决了---
原来请求的内容里有字面上的 r"\n"
(表示为 "\\n"
)字符,因为我直接复制粘贴了这个内容作为Python字符串,所以Python误以为我在给它换行符,而不是转义的换行符。
这会导致 Bad Request
的原因是这样的:请求的内容是JSON格式,而在JSON中,所有的换行符都必须被转义。因此,当服务器从原始文本中加载JSON对象时,就会抛出错误,导致 Bad Request
。
我意识到这一点是因为在两种情况下,Content-Length(内容长度)头部是不同的(\n
是一个字符,而 \\\n
是两个字符,虽然Content-Length可能并不重要)。
另外值得注意的是,当发送的Content-Length较低时,也会返回 Bad Request
。我认为这是因为JSON内容被截断,服务器无法接受重要的字符(比如闭合括号等)。
--- 问题:---
总结:
我正在尝试使用Python模拟在我的Firefox浏览器中对bitbucket.org进行的POST请求。以下是我所做的:
- 使用Firebug跟踪POST请求
- 复制POST请求的头部信息
- 复制POST请求的内容(以application/json格式)
代码:
这是我用来发送请求的代码,但有点长,不太相关。我的Content-Type是application/json,而我的POST内容是一个JSON编码的字符串。
dataString = '{"branch":"master","files":[{"path":"readme.txt","content":"ntestxx\n \n"}],"message":"readme.txt edited online with Bitbucket","parents":["465305dc4da32f91da057b65297cda9b72c"],"repository":{"full_name":"minesite/ica-i18n"},"timestamp":"2014-03-20T23:49:29.759Z","transient":false}'
headers = {'X-CSRFToken': '6TqWjCl698U99Iu6ZYGBAloCxZ', 'Content-Length': '2190', 'Accept-Language': 'en,en-us;q=0.7,zh;q=0.3', 'X-NewRelic-ID': 'VwMGVVZSGwIIUFBQDwU=, VwMGVVZSGwIIUFBQDwU=', 'Cookie': 'csrftoken=6TqWjCl698U99Iu6ZYGBAloCxZ; __utma=254090395.1171276563.1394767875.1394776803.1395358874.3; __utmc=254090395; __utmz=254090395.1394776803.2.2.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); bb_session=gpqergylgoa7icpwosqsbpxig0; __utmv=254090395.|1=isBBUser=true=1; recently-viewed-repos_1701252=3802872%2C108928; __utmb=254090395.21.9.1395359363952', 'Connection': 'keep-alive', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:27.0) Gecko/20100101 Firefox/27.0', 'Host': 'bitbucket.org', 'X-Requested-With': 'XMLHttpRequest', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Referer': 'https://bitbucket.org/xxxxxx/xxxxxxx/src/465305dc4da32f91da057b6529a8e4/readme.txt?at=master', 'Content-Type': 'application/json; charset=UTF-8', 'Accept-Encoding': 'gzip, deflate'}
edit = requests.post("https://bitbucket.org/!api/internal/repositories/xxxxxxx/xxxxxxxx/oecommits/", data=dataString, headers=headers)
结果与预期结果:
当我使用Firefox浏览器(通过Firebug的“重新发送请求”功能)执行POST请求时,我得到了一个409 CONFLICT的响应(这是我想要的响应!我正在模拟对在线编辑器的请求,所以这应该是对重新发送编辑的正确响应)。
然而,当我尝试通过复制请求头和请求内容来模拟请求时,我得到了一个400 BAD REQUEST的响应,而且响应中没有其他信息,所以我甚至不知道我的问题是什么。
无论我在浏览器中发送POST请求多少次(尽管时间戳不正确),都能达到预期效果,但服务器拒绝接受我使用Python请求库发出的任何请求。
使用浏览器请求的响应:
头部信息
HTTP/1.1 409 CONFLICT
Server: nginx/1.5.10
Date: Fri, 21 Mar 2014 00:20:55 GMT
Content-Type: text/plain
Content-Length: 45
Connection: keep-alive
x-served-by: app16
X-Render-Time: 0.558492183685
Content-Language: en
X-Static-Version: 48695e7c3140
Vary: Authorization, Accept-Language, Cookie
X-Version: e6778a5040f7
Etag: "92f0b780984e984140de0f8ed0a3992c"
X-Frame-Options: SAMEORIGIN
X-Request-Count: 483
X-NewRelic-App-Data: PxQEVFdXCAITVVlWBgMPUkYdFGQHBDcQUQxLA1tMXV1dSn8UXwJHCwtYGAMPF1pGUw8EFhlQRxYXH1dDC0gKDEQHSgxZVBpaUgtdDVQTQFgrWFsICAZ9V1kQIg1aXF4SLFBYVw4DEUxTEF0DTF0WHgNJCU8EVApUUgUHVFFQCgQCU1FXGwMGX1QdFAEBUVVbA1AJVQEBB1FSA11DHQdSDhdTag==
内容
Specified change not on head of branch master
使用Python请求的响应:
头部信息
content-length: 11
x-served-by: app10
x-render-time: 0.012787103653
content-language: en
content-type: text/plain
vary: Authorization, Accept-Language, Cookie
connection: keep-alive
server: nginx/1.5.10
x-version: e6778a5040f7
etag: "825644f747baab2c00e420dbbc39e4b3"
x-request-count: 321
x-newrelic-app-data: PxQEVFdXCAITVVlWBgMPUkYdFGQHBDcQUQxLA1tMXV1dSn8UXwJHCwtYGAMPF1pGUw8EFhlQRxYXH1dDC0gRB0MNTRBbXQ5gVhZWFEMCVkBIBhtRSFMJAARQUlsDBw9VXAIBC1tWVU4CUwtUFBpVAwFcWgdTVQIAXQBRWQQAGh9WBQ0RUmw=
date: Fri, 21 Mar 2014 00:51:01 GMT
x-frame-options: SAMEORIGIN
x-static-version: 48695e7c3140
内容
Bad Request
我的一些想法:
我在想,也许HTTP POST请求还有其他组件我需要模拟?也许当Firefox发送POST请求时,会添加一些头部或包装,使请求有效?
或者说POST请求不仅仅是方法、头部和内容,还有其他东西?
也许这与它是HTTPS而不是HTTP有关?
更新:
我尝试将“发送的cookies”一起发送,但效果不大。
1 个回答
POST请求除了方法、头部和主体,还有其他重要的东西吗?
没有。最重要的部分是请求的头部。这两种情况的头部应该完全一样。
因为Firebug只能在Firefox浏览器里追踪网络请求,所以你需要一个外部的网络分析工具,比如Wireshark,来追踪你Python脚本发出的请求。当然,你需要在脚本所在的服务器上运行这个工具。
另一种解决办法是把请求发到一个本地的网页服务器上,并在那记录请求的信息。
这样你就可以比较浏览器发出的请求和你脚本发出的请求了。