最近,我将客户端上传代码从HTTPConnection
移植到requests
。上传图片时:
file_name ='/path/to/216169_1286900924tait.jpg?pt=5&ek=1'
存储在磁盘上的图像实际上是名称,我想将它上传到具有相同路径和名称的远程服务器,所以我构建了这样的请求:
url = 'http://host/bucket_name/%s' % (file_name)
headers = {...} # some other headers
with open(file_name, 'rb') as fd:
data = fd.read()
r = requests.put(url, data=data, headers=headers)
assert(r.status_code==200)
....
但发送给服务器的请求改为:
/path/to/216169_1286900924tait.jpg
requests
应将尾部编码为%3Fpt%3D5%26ek%3D1
,但似乎requests
对url
的url-encode不执行任何操作,我认为它可能与{{1}匹配模式请求参数,如何在没有模式匹配的情况下使?pt=5&ek=1
盲目地转换网址?
服务器获取修剪后的网址并使用它计算签名,因此与我使用原始网址计算的签名不匹配,因此403返回。
答案 0 :(得分:0)
您可能在构建网址的方式上遇到问题:
>>> payload = {'pt': 5, 'ek': '1'}
>>> r = requests.get('http://host/bucket_name/file_name', params=payload)
如果你打印(r.url),你应该有正确的表格。
答案 1 :(得分:0)
为什么requests
会假设对查询参数进行编码?它不知道您不希望将该部分URL视为查询字符串。此外,请求按原样发送到服务器,查询字符串不会如您所建议的那样省略。您可以使用nc
验证该内容:
# run nc server
$ nc -l 1234
# then send request from Python
>>> requests.put('http://localhost:1234/path/to/216169_1286900924tait.jpg?pt=5&ek=1', data='any old thing')
nc
将显示请求:
PUT /path/to/216169_1286900924tait.jpg?pt=5&ek=1 HTTP/1.1 Host: localhost:1234 Content-Length: 13 User-Agent: python-requests/2.9.1 Connection: keep-alive Accept: */* Accept-Encoding: gzip, deflate any old thing
因此远程服务器(正确地根据HTTP协议)将文件名的?pt=5&ek=1
部分解释为查询参数。它还应该做什么?
为了进行比较,我认为它之前曾与httplib.HTTPConnection
合作:
>>> import httplib
>>> r = httplib.HTTPConnection('localhost', 1234)
>>> r.request('PUT', '/path/to/216169_1286900924tait.jpg?pt=5&ek=1', 'hello from httplib')
生成此请求:
PUT /path/to/216169_1286900924tait.jpg?pt=5&ek=1 HTTP/1.1 Host: localhost:1234 Accept-Encoding: identity Content-Length: 18 hello from httplib
请注意,发送网址的方式没有区别。
答案 2 :(得分:0)
我深入研究了requests
源代码,我找到了以下代码行(是的,requests
基于urllib3
):
scheme, auth, host, port, path, query, fragment = urllib3.util.parse_url(url)
看起来,您应该在构建网址字符串时手动对网址进行网址编码,例如:
>>> path = '''~!@#$^&*()_+|}{":?><`-=\\][\';.,'''
>>> url = 'http://host.com/bucket/%s' % path
>>> urllib3.util.parse_url(url)
>>> Url(scheme='http', auth=None, host='host.com', port=None, path='/bucket/~!@', query=None, fragment='$^&*()_+|}{":?><`-=B%7C%7D%7B%22%3A%3F%3E%3C%60-%3D%5C%5D%5B%27%3B.%2C')
如果您对path
进行编码,请注意path
字段输出不与path
相同:
>>> path = '''~!@#$^&*()_+|}{":?><`-=\\][\';.,'''
>>> url = 'http://host.com/bucket/%s' % (urllib.quote(path, ''))
>>> print url
>>> http://host.com/bucket/%7E%21%40%23%24%25%5E%26%2A%28%29_%2B%7C%7D%7B%22%3A%3F%3E%3C%60-%3D%5C%5D%5B%27%3B.%2C
>>> urllib3.util.parse_url(url)
>>> Url(scheme='http', auth=None, host='host.com', port=None, path='/bucket/%7E%21%40%23%24%25%5E%26%2A%28%29_%2B%7C%7D%7B%22%3A%3F%3E%3C%60-%3D%5C%5D%5B%27%3B.%2C', query=None, fragment=None)
这就是我想要的。但是如果你想将unicode字符传递给path
,你不需要对它们进行编码,它们会自动转换为%xx%xx
格式。但是对于你传入URL的任何字符,url encode都是一个很好的建议。