最近在做一個資產管理系統項目,其中有一個部分是客戶端向服務端發送采集到的數據的,服務端是Django寫的,客戶端需要用requests模塊模擬發送請求
假設發送的數據是這樣的:
data = {'status' : True , 'content' : { 'k1' : 'xxxx' , 'k2' : 'xxxx' }}
是字典套字典的,然后通過requests.post(url = url ,data = data) 發送,會發現在服務端接收到的數據是這樣的,<QueryDict>:{ [ 'status' , 'content' ] }
發生了什么,為什么只取到了key,但是沒有取到values呢,既然能取到key,說明Django是沒問題的
那問題肯定就出在requests.post發送數據上了
好了,鋪墊完畢
想一下,ajax是怎么發送數據的
$.ajax ({
url : 'xxxx',
data : {
k1 : 1,
k2 : 'abc',
k3 : [1,2,3,4,'s']
}
})
吶,只能發送這三種格式吧,沒有字典吧,如果非要發送字典,可以通過JSON把字典轉成字符串格式,JSON.stringfy({ 'k1':'xxx' })
form 表單也是這樣的,而requests.post就是仿造的form表單提交
發現了問題,我們再來剖析一下http發送post請求的過程
ajax發送的data是個字典,是鍵值對的形式,在http的post請求過程中,把這種鍵值對轉換成
k1=xxx&k2=xxx這種格式,並且會帶上一個請求頭:
content-type : application/x-www-form-urlencoded
這個請求頭和k1=xxx&k2=xxx這種格式是以一一對應的,只要發送這個格式,就必須帶上這個請求頭,后台的request.POST會根據這個請求頭解析這種格式,還原成以前字典的格式。而且這種格式和這個請求頭都是默認的,所以如果我們不修改,直接發送文章開始說的那種格式,服務端就不能解析,所以只拿到字典的key,沒拿到value
但如果是現在這種情況,我們發送的不是這個格式,而是想要發送JSON.stringfy({ 'k1':'xxx' })這種json字符串,也是有辦法的,ajax可以自定義請求頭,改成這樣:
$.ajax ({
url : 'xxxx',
headers : { 'content-type' : 'application/json' },
data : JSON.stringfy( {k1 : 1,k2 : 'abc',k3 : [1,2,3,4,'s']})
})
這樣后台就會根據json去解析接收到的數據(json格式的字符串)
但是不能用request.POST了,因為request.POST 默認是按k1=xxx&k2=xxx這種格式解析數據的,
應該用request.body去拿數據,再用json.loads一下就拿到這個字典了
默認情況下,form 表單,ajax 以及 requests.post 都是這樣處理的
所以在這個項目中,我們需要用requests自定義這個請求頭,這個寫法就很簡單了
requests.post(url = url ,json = data) ,就行了,把原來的data關鍵字參數換成json關鍵字參數。
內部做的就是,1.序列化data,2.帶一個請求頭'content-type' : 'application/json'
這樣,服務端拿到的數據,先decode解碼一下,在json.loads,就是我們熟悉的字典了
在ajax中,像后台發送數據:
GET請求:
1.如果是字符串,數字,可以正常發送:
$.ajax({
url : 'xxxx',
type : 'get',
data : { 'k1':'v1','k2':'v2' }
....
})
2.如果是字典(數組)格式
a.發送的格式為:'test_list':[1,2,3,'abc']
后台request.GET 拿到的數據是這樣的:
'test_list[]': ['1', '2', '3', 'abc']
b.ajax發送時添加一個traditional:true
后台request.GET 拿到的數據是這樣的:
'test_list': ['1', '2', '3', 'abc']
POST請求:
發送的數據格式:
var test_list = [1,2,'abc']; var test_dict = {'kk1':'vv1','kk2':'vv2'}
data: {'k1':123,'k2':'abc','k3':test_list,'k4':test_dict}
后台依次打印
print(request.POST) print(json.loads(request.body.decode('utf-8'))) print(request.body)
1.就直接這樣發送:
后台拿到的數據:
<QueryDict: {'k1': ['123'], 'k2': ['abc'], 'k3[]': ['1', '2', 'abc'], 'k4[kk1]': ['vv1'], 'k4[kk2]': ['vv2']}>
報錯
b'k1=123&k2=abc&k3%5B%5D=1&k3%5B%5D=2&k3%5B%5D=abc&k4%5Bkk1%5D=vv1&k4%5Bkk2%5D=vv2'
2.加上traditional:true
<QueryDict: {'k1': ['123'], 'k2': ['abc'], 'k3': ['1', '2', 'abc'], 'k4': ['[object Object]']}>
報錯
b'k1=123&k2=abc&k3=1&k3=2&k3=abc&k4=%5Bobject+Object%5D'
3.ajax通過JSON.stringify( ) 發送
前端:
data: JSON.stringify({'k1':123,'k2':'abc','k3':test_list,'k4':test_dict})
后台拿到的數據:
<QueryDict: {'{"k1":123,"k2":"abc","k3":[1,2,"abc"],"k4":{"kk1":"vv1","kk2":"vv2"}}': ['']}>
{'k1': 123, 'k2': 'abc', 'k3': [1, 2, 'abc'], 'k4': {'kk1': 'vv1', 'kk2': 'vv2'}}
b'{"k1":123,"k2":"abc","k3":[1,2,"abc"],"k4":{"kk1":"vv1","kk2":"vv2"}}'
