原创作品:首发u3v3, 转载请保留
作者ID:Yi_Zhi_Yu
首发日期:2017.4.13
Python学习群:278529278 (欢迎交流)
前言
在PHP中, 我们使用curl 扩展发送post请求时, 可以通过http_build_query 来构造多维的 post 参数, 用法如下
$request_params = [
'name' => 'Yi_Zhi_Yu',
'scores' => [
['name' => 'English', 'score'=>100],
['name' => 'Math', 'score'=>100]
]];
echo http_build_query($request_params);
输出如下
user=Yi_Zhi_Yu&info%5B0%5D%5Bage%5D=27&info%5B0%5D%5Bsex%5D=man
通过url decode, 即
user=Yi_Zhi_Yu&info[0][age]=27&info[0][sex]=man
这就是post时, 我们需要发送的post body 的内容
PHP 里有http_build_query, 那python中呢,
当然没了, 要不然我就不用自己实现了
问题
python 里, 我们发送post的时候, 如果需要将post body 做url encode, 有一个urllib2模块可以用
if __name__ == '__main__':
request_params = {'name': 'Yi_Zhi_Yu', 'score': 100}
print(urllib.parse.urlencode(request_params))
# name=Yi_Zhi_Yu&score=100
那如果 post 的结构是多维的呢? 看下
if __name__ == '__main__':
request_params = {
'name': 'Yi_Zhi_Yu',
'scores':[
{'name': 'English', 'score': 100},
{'name': 'Math', 'score': 100}
]
}
print(urllib.parse.urlencode(request_params))
输出如下
name=Yi_Zhi_Yu&scores=%5B%7B%27name%27%3A+%27English%27%2C+%27score%27%3A+100%7D%2C+%7B%27name%27%3A+%27Math%27%2C+%27score%27%3A+100%7D%5D
decode 可视化后:
name=Yi_Zhi_Yu&scores=[{'name': 'English', 'score': 100}, {'name': 'Math', 'score': 100}]
name 还算正常, scores直接是个json格式了
post 请求发送出去后, 需要服务端 decode 才能用
我们期望的格式应该是类似于PHP http_build_query的结果
name=Yi_Zhi_Yu&scores[0][name]=English&scores[0][score]=100&scores[1][name]=Math&scores[1][score]=100
服务端通过POST获取后, 直接就可以作为数组使用
为此, 我实现了一个简单的版本
解决
直接看代码
import urllib.parse
def url_encoder(params):
g_encode_params = {}
def _encode_params(params, p_key=None):
encode_params = {}
if isinstance(params, dict):
for key in params:
encode_key = '{}[{}]'.format(p_key,key)
encode_params[encode_key] = params[key]
elif isinstance(params, (list, tuple)):
for offset,value in enumerate(params):
encode_key = '{}[{}]'.format(p_key, offset)
encode_params[encode_key] = value
else:
g_encode_params[p_key] = params
for key in encode_params:
value = encode_params[key]
_encode_params(value, key)
if isinstance(params, dict):
for key in params:
_encode_params(params[key], key)
return urllib.parse.urlencode(g_encode_params)
看测试代码
if __name__ == '__main__':
request_params = {'name': 'Yi_Zhi_Yu',
'scores':[
{'name': 'English', 'score': 100},
{'name': 'Math', 'score': 100}
]
}
#print(urllib.parse.urlencode(request_params))
print(url_encoder(request_params))
输出结果经过decode后如下
scores[1][score]=100&scores[0][score]=100&scores[1][name]=Math&scores[0][name]=English&name=Yi_Zhi_Yu
服务端在接受到这个POST bod以后, 也可以直接作为数组使用, 请求格式与PHP的 http_build_query基本类似
不过这里有个问题是, url encode后的参数顺序与原始的request params 不一样了
这个是因为python的dict本身是无序的, 如果要求有序, 大家可以把默认的dict 替换为OrderDict
这个作为一个小作业吧, 后续有时间, 我会贴出来结果的
:)
误区
这里还有一个关于PHP的常见误区, 当客户端发起的post请求中, header 的content-type 被设置成了 application/json时, 很多人认为, 在PHP的Server端, 用$_POST就能获取json的请求格式数据了
然而并非如此, 这种时候, PHP只能通过
$json = file_get_contents('php://input');
$obj = json_decode($json);
获取 json 的数据