我們在做介面測試之前,先需要根據介面文件或抓包介面資料,搞清楚被測介面的詳細內容,其中就包含請求引數的編碼格式,從而使用對應的引數格式傳送請求。例如某個介面規定的請求主體的編碼方式為 application/json,那麼在請求該介面時,請求引數格式必須是 json 格式,使用其他的編碼方式請求不會成功。
那麼,在http請求中,請求主體常用的編碼方式有哪些?每種編碼方式在python中需要對應使用什麼樣格式的請求引數?這便是我們們在本篇部落格要弄明白的地方。
content-type
在POST請求中,由content-type指定請求引數的格式,所以,為了搞清楚請求引數的編碼格式,我們有必要了解HTTP請求頭資訊中的 content-type 欄位。
content-type的作用
在HTTP協議中,報文通常包括兩個部分 請求頭部(head)、請求主體(body),其中 body 可以為空,如 GET 請求是將請求引數放在請求URL中而不是放在 body 中。
而POST請求中的請求引數則是放在 body 中,接收請求的一端(也就是伺服器)需要知道傳過來的 body 是什麼型別的資料,採用怎樣的編碼方式,才能對資料進行對應的解析,這時就需要在請求頭中使用 content-type 來指明 body 的媒體型別。
媒體型別
媒體型別(通常稱為 Multipurpose Internet Mail Extensions 或 MIME 型別 )是一種標準,用來表示文件、檔案或位元組流的性質和格式。它在IETF RFC 6838中進行了定義和標準化。
瀏覽器通常使用MIME型別(而不是副檔名)來確定如何處理URL,因此Web伺服器在響應頭中新增正確的MIME型別非常重要。如果配置不正確,瀏覽器可能會曲解檔案內容,網站將無法正常工作,並且下載的檔案也會被錯誤處理。
而瀏覽器傳過來的內容也需要指定媒體型別,伺服器才能根據媒體型別做對應的資料解析。
常見的媒體型別如下:
另外還有 multipart 型別,表示細分領域的檔案型別的種類,經常對應不同的 MIME 型別,用於檔案的上傳,分為如下兩種:
multipart/form-data
multipart/byteranges
這裡均只做簡單說明,具體每種型別的使用詳情可自行查詢相關資料。
總之,在介面測試中,請求頭中的 Content-Type 作用就是,用來告知服務端請求body的編碼方式。GET請求因為body為空,所以在GET請求中沒有Content-Type欄位。
get請求引數格式
我們已經知道GET請求的請求引數是直接放在URL中的,且不需要content-type指定媒體型別。而GET請求中的請求引數的編碼格式為query string params。
query string params
說明
query string params 格式,引數會以 url string 的形式進行傳遞,即?
後的字串則為其請求引數,並以&
作為分隔符,引數編寫方式為?key=value&key=value
,拼接在 url 後面。通常用於GET請求,除此之外其他有些請求方式也可以使用這種格式。
在瀏覽器中開啟百度,搜尋給你一頁白紙-部落格園
,通過F12抓包也可以看到 Payload 中請求引數的格式為 Query String Parameters,如下圖:
即開啟URL連結https://www.baidu.com/s?ie=utf-8&wd=給你一頁白紙-部落格園
。
python程式碼傳送請求
如果使用python中的 requests.get() 對上圖示例傳送get請求,則需使用引數 params,引數值為dict(字典)格式即可,示例如下:
import requests
url = "http://www.baidu.com/s"
params = {"wd": "給你一頁白紙-部落格園", "ie": "utf-8"}
res = requests.get(url=url, params=params)
print(res.text)
GET請求中請求主體編碼格式固定,在做介面測試時基本無需確定其編碼格式,較為簡單。
post請求引數格式
對於HTTP協議中的POST請求,其請求引數有不同的編碼格式。
服務端通常是根據請求頭(headers)中的 Content-Type 欄位來獲知請求中的訊息主體是用何種方式編碼 (即媒體型別),再對請求引數進行對應方式的解析。
post請求常見的編碼方式有四種:
-
application/x-www-form-urlencoded
-
multipart/form-data
-
application/json
-
text/xml
application/x-www-form-urlencoded
application/x-www-form-urlencoded 是最常見的 POST 提交資料的方式,用於表單資料的提交。瀏覽器的原生form表單,如果不設定enctype屬性,那麼最終就會預設以 application/x-www-form-urlencoded 方式提交資料。
說明
POST請求使用 application/x-www-form-urlencoded 對請求引數進行編碼時,有以下特點:
-
請求頭header中content-type的值為:application/x-www-form-urlencoded
-
請求引數會按照 key1=value1&key2=value2 的方式進行編碼,且 key 和 value 都進行了 URL 轉碼
-
伺服器收到請求後,會對應的方式對這種編碼格式的請求引數進行解析
-
雖然都將請求引數進行了 key1=value1&key2=value2 形式編碼,但GET請求中會將編碼後的內容拼接在URL後。
以請求TesterHome網登陸介面為例:
Request Headers中的 content-type 為 application/x-www-form-urlencoded; charset=UTF-8。
python程式碼傳送請求
使用python中的 requests.post() 請求上面圖片中的介面時,需使用引數data,引數值為dict(字典)格式即可,程式碼如下:
import requests
'''
請求頭的content-type為application/x-www-form-urlencoded
'''
data = {
"user[login]": "賬號",
"user[password]": "密碼",
"user[remember_me]": 0,
"commit": "登入"
}
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
}
url = "https://testerhome.com/account/sign_in"
# 編碼格式為application/x-www-form-urlencoded;charset=UTF-8,使用data引數,引數值為dict,
res = requests.post(url=url, headers=headers, data=data)
print(res.text)
multipart/form-data
multipart/form-data 也是一個常見的 POST 資料提交的方式,用於上傳檔案。我們使用表單上傳檔案時,必須讓 form 表單的enctype等於 multipart/form-data。
說明
POST 請求使用 multipart/form-data 對請求引數進行編碼時,有以下特點:
-
使用 boundary 用於分割不同的欄位
-
訊息主體中按照欄位個數又分為多個結構類似的部分,每部分都以--boundary開始,緊接著下一行是內容描述資訊,再下一行是欄位具體內容(文字或二進位制)。如果傳輸的是檔案,還要包含檔名和檔案型別資訊
-
訊息主體最後以 --boundary-- 標示結束
示例如下:
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundary8G1vtgT1pXWqqHzV
------WebKitFormBoundary8G1vtgT1pXWqqHzV
Content-Disposition: form-data; name="txt"
title
------WebKitFormBoundary8G1vtgT1pXWqqHzV
Content-Disposition: form-data; name="file"; filename="blog.png"
Content-Type: image/png
PNG ... content of blog.png ...
------WebKitFormBoundary8G1vtgT1pXWqqHzV--
python程式碼傳送請求
例如,我們在請求牛圖網的上傳圖片的介面時,Requests Headers 中為content-type: multipart/form-data; boundary=----WebKitFormBoundary4aA3ZrkOVwUIvmx0
,如下圖所示:
在python中使用requests.post()請求該介面,程式碼示例如下:
import requests
'''
請求頭的content-type為multipart/form-data
'''
def post_mulitpart_form_data():
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
"content-type": "multipart/form-data; boundary=----WebKitFormBoundary4aA3ZrkOVwUIvmx0"
}
url = "https://www.niupic.com/api/upload"
filepath = "./dianzan.jpg"
# 讀取上傳檔案的內容
files = {"file": open(filepath, "rb")}
# 使用files引數接收請求內容,即讀取的上傳檔案內容
res = requests.post(url=url, headers=headers, files=files).content
print(json.loads(res))
if __name__ == '__main__':
post_mulitpart_form_data()
執行結果如下:
C:\Users\xiaoqq\AppData\Local\Programs\Python\Python37\python.exe E:/blog/python介面自動化/flask_demo/test.py
{'status': 'success', 'code': 200, 'data': 'https://i.niupic.com/images/2022/02/20/9V3n.jpg', 'msg': '上傳成功!'}
Process finished with exit code 0
application/x-www-form-urlencoded 與 multipart/form-data 這兩種 POST 請求的資料編碼方式,都是瀏覽器原生支援的,且現階段標準中原生form表單也只支援這兩種方式(通過form元素的enctype屬性指定,預設為 application/x-www-form-urlencoded。其實enctype還支援text/plain,不過用得非常少)。
application/json
在一般公司的普通業務場景中,application/json 很常見,用來告訴服務端,訊息主體是序列化後的json字串,即前端傳給服務端的資料是json格式的。
也就是說,如果請求頭中 content-type 為 application/json,那麼我們在使用工具如postman或python指令碼模擬請求介面時,請求引數也需要先轉換成json格式,然後才能傳送請求。程式碼示例如下:
import requests
import json
'''
請求頭的content-type為application/json
'''
headers = {"Content-Type": "application/json;charset=utf8"}
url = "http://127.0.0.1:5000/login"
_data = {
"username": "lilei",
"password": "123456"
}
# 這裡使用json引數,即json=_data
res = requests.post(url=url, headers=headers, json=_data).text
# 當然還可以使用data引數,但需先將_data轉換為json格式,即data=json.dumps(_data)
# json.dumps()將dict格式轉換成json格式
res = requests.post(url=url, headers=headers, data=json.dumps(_data)).text
print(res)
text/xml
post請求中,有些請求主體的編碼格式為 text/xml,即請求頭中content-type 欄位對應值為 text/xml,對於這樣的介面,我們需要使用xml格式的引數去傳送請求。
使用 requests.post() 傳送請求引數為xml
格式的post請求時,只需要將xml檔案中的body部分寫成一個字串型別就行,遇到換行時在後面加個反斜槓,並將這個字串賦值給data
引數。程式碼示例如下:
import requests
'''
請求頭的content-type為text/xml
'''
def post_text_xml():
headers = {"Content-Type": "text/xml"}
url = "http://httpbin.org/post"
body = '<?xml version="1.0" encoding = "UTF-8"?>' \
'<COM>' \
'<REQ name="給你一頁白紙">' \
'<USER_ID></USER_ID>' \
'<COMMODITY_ID>111111</COMMODITY_ID>' \
'<SESSION_ID>asdfghjklfr0123</SESSION_ID>' \
'</REQ>' \
'</COM>'
res = requests.post(url=url, headers=headers, data=body.encode("utf-8")).text
print(res)
if __name__ == '__main__':
post_text_xml()
執行結果如下:
C:\Users\xiaoqq\AppData\Local\Programs\Python\Python37\python.exe E:/blog/python介面自動化/flask_demo/test.py
{
"args": {},
"data": "<?xml version=\"1.0\" encoding = \"UTF-8\"?><COM><REQ name=\"\u7ed9\u4f60\u4e00\u9875\u767d\u7eb8\"><USER_ID></USER_ID><COMMODITY_ID>111111</COMMODITY_ID><SESSION_ID>asdfghjklfr0123</SESSION_ID></REQ></COM>",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "182",
"Content-Type": "text/xml",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.24.0",
"X-Amzn-Trace-Id": "Root=1-6211ebd3-2cc90293777649ba01e50b08"
},
"json": null,
"origin": "101.71.37.212",
"url": "http://httpbin.org/post"
}
Process finished with exit code 0
總結
這裡只介紹了HTTP協議中 GET請求 和 POST請求 常見的請求引數編碼格式,怎樣確定請求引數的編碼格式,以及在python程式碼中使用requests傳送請求時,需要使用怎樣格式的引數。
我們總結如下:
-
GET請求,請求引數編碼格式為query string params,requests.get() 傳送請求時使用params引數,params的值 (即請求引數) 為字典格式
-
POST請求主體的編碼格式需要根據請求頭中的content-type欄位確定
-
content-type: application/x-www-form-urlencoded,requests.post() 傳送請求時使用data引數,data的值 (即請求引數) 為字典格式
-
content-type: multipart/form-data,requests.post() 傳送請求時使用files引數,files的值 (即請求引數) 即為讀取的上傳檔案的內容
-
content-type: application/json,requests.post() 傳送請求時使用json引數,json的值 (即請求引數) 為字典格式,或者也可以使用 data 引數,但此時需要先將請求引數轉換為json格式
-
content-type: text/xml,requests.post() 傳送請求時使用data引數,data的值 (即請求引數) 為 xml 中的body部分內容
具體的指令碼編寫方法參考示例程式碼。