python+pytest介面自動化(6)-請求引數格式的確定

給你一頁白紙發表於2022-03-10

我們在做介面測試之前,先需要根據介面文件或抓包介面資料,搞清楚被測介面的詳細內容,其中就包含請求引數的編碼格式,從而使用對應的引數格式傳送請求。例如某個介面規定的請求主體的編碼方式為 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請求常見的編碼方式有四種:

  1. application/x-www-form-urlencoded

  2. multipart/form-data

  3. application/json

  4. 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 對請求引數進行編碼時,有以下特點:

  1. 請求頭header中content-type的值為:application/x-www-form-urlencoded

  2. 請求引數會按照 key1=value1&key2=value2 的方式進行編碼,且 key 和 value 都進行了 URL 轉碼

  3. 伺服器收到請求後,會對應的方式對這種編碼格式的請求引數進行解析

  4. 雖然都將請求引數進行了 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 對請求引數進行編碼時,有以下特點:

  1. 使用 boundary 用於分割不同的欄位

  2. 訊息主體中按照欄位個數又分為多個結構類似的部分,每部分都以--boundary開始,緊接著下一行是內容描述資訊,再下一行是欄位具體內容(文字或二進位制)。如果傳輸的是檔案,還要包含檔名和檔案型別資訊

  3. 訊息主體最後以 --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傳送請求時,需要使用怎樣格式的引數。

我們總結如下:

  1. GET請求,請求引數編碼格式為query string params,requests.get() 傳送請求時使用params引數,params的值 (即請求引數) 為字典格式

  2. POST請求主體的編碼格式需要根據請求頭中的content-type欄位確定

  3. content-type: application/x-www-form-urlencoded,requests.post() 傳送請求時使用data引數,data的值 (即請求引數) 為字典格式

  4. content-type: multipart/form-data,requests.post() 傳送請求時使用files引數,files的值 (即請求引數) 即為讀取的上傳檔案的內容

  5. content-type: application/json,requests.post() 傳送請求時使用json引數,json的值 (即請求引數) 為字典格式,或者也可以使用 data 引數,但此時需要先將請求引數轉換為json格式

  6. content-type: text/xml,requests.post() 傳送請求時使用data引數,data的值 (即請求引數) 為 xml 中的body部分內容

具體的指令碼編寫方法參考示例程式碼。

相關文章