原始碼目錄結構
ApiResponse
這個類沒啥好說的
class ApiResponse(Response):
"""
繼承了requests模組中的Response類,重寫了裡面的raise_for_status方法
"""
def raise_for_status(self):
if hasattr(self, "error") and self.error:
raise self.error
Response.raise_for_status(self)
get_req_resp_record
這個函式的功能是獲取請求記錄和響應記錄,原始碼分為4段來看
第1段
def get_req_resp_record(resp_obj: Response) -> ReqRespData:
"""
:param resp_obj: Response響應
:return: 返回自定義的ReqResData模型類
"""
def log_print(req_or_resp, r_type):
"""
日誌列印,格式為標準的json
"""
msg = f"\n================== {r_type} details ==================\n"
for key, value in req_or_resp.dict().items():
# 如果value中還包含著dict或者list,就把value轉成json格式
if isinstance(value, dict) or isinstance(value, list):
value = json.dumps(value, indent=4, ensure_ascii=False)
msg += "{:<8} : {}\n".format(key, value)
logger.debug(msg)
第1段程式碼就是定義了一個列印日誌的函式,列印的日誌解析為標準的json格式
第2段
# 記錄實際請求資訊(請求頭、cookie資訊、請求體)
request_headers = dict(resp_obj.request.headers)
request_cookies = resp_obj.request._cookies.get_dict()
request_body = resp_obj.request.body
if request_body is not None:
try:
request_body = json.loads(request_body)
except json.JSONDecodeError:
# str: a=1&b=2
pass
except UnicodeDecodeError:
# bytes/bytearray: request body in protobuf
pass
except TypeError:
# neither str nor bytes/bytearray, e.g. <MultipartEncoder>
pass
# lower_dict_keys的作用是將字典中的key大寫轉小寫
request_content_type = lower_dict_keys(request_headers).get("content-type")
if request_content_type and "multipart/form-data" in request_content_type:
# upload file type
request_body = "upload file stream (OMITTED)"
request_data = RequestData(
method=resp_obj.request.method,
url=resp_obj.request.url,
headers=request_headers,
cookies=request_cookies,
body=request_body,
)
# 在debug模式下列印請求日誌
log_print(request_data, "request")
第2段程式碼是先獲取request_headers
、request_cookies
、request_body
,然後將獲取到的資訊放入RequestData
模型中,最後列印請求的資訊
第3段
# 記錄響應資訊
resp_headers = dict(resp_obj.headers)
lower_resp_headers = lower_dict_keys(resp_headers)
content_type = lower_resp_headers.get("content-type", "")
if "image" in content_type:
# response is image type, record bytes content only
response_body = resp_obj.content
else:
try:
# try to record json data
response_body = resp_obj.json()
except ValueError:
# only record at most 512 text charactors
resp_text = resp_obj.text
response_body = omit_long_data(resp_text)
response_data = ResponseData(
status_code=resp_obj.status_code,
cookies=resp_obj.cookies or {},
encoding=resp_obj.encoding,
headers=resp_headers,
content_type=content_type,
body=response_body,
)
# 在debug模式下列印響應日誌
log_print(response_data, "response")
第3段程式碼是獲取resp_headers
、content_type
、response_body
,最後將這些資料都放入ResponseData
模型類中,最後列印響應日誌
第4段
req_resp_data = ReqRespData(request=request_data, response=response_data)
return req_resp_data
最後這段就是將剛才的請求資訊和響應資訊全部放入ReqRespData
模型中,最後get_req_resp_record
函式返回的內容就是ReqRespData
模型
HttpSession
在requests.Session
上進行了二次封裝,該類包含4個方法,下面依次介紹
init
def __init__(self):
super(HttpSession, self).__init__()
self.data = SessionData()
初始化方法,定義了data屬性的預設值為SessionData
模型,該模型包含了req_resps: List[ReqRespData] = []
請求響應內容
update_last_req_resp_record
def update_last_req_resp_record(self, resp_obj):
"""
update request and response info from Response() object.
"""
# TODO: fix
self.data.req_resps.pop()
self.data.req_resps.append(get_req_resp_record(resp_obj))
更新最新的請求響應記錄,放入req_resps
列表中
request
傳送requests.Request
請求,返回requests.Response
響應,還做了以下事情
- 1.設定了超時時間120s
- 2.計算整個請求花費了多少時間
- 3.定義了客戶端ip地址和埠號、服務端ip地址和埠號
- 4.計算了響應體的內容大小
- 5.記錄了消耗時間
- 6.記錄了request和response記錄,包括重定向記錄
_send_request_safe_mode
傳送一個http請求,並捕獲由於連線問題可能發生的任何異常
def _send_request_safe_mode(self, method, url, **kwargs):
"""
Send a HTTP request, and catch any exception that might occur due to connection problems.
Safe mode has been removed from requests 1.x.
"""
try:
return requests.Session.request(self, method, url, **kwargs)
except (MissingSchema, InvalidSchema, InvalidURL):
raise
except RequestException as ex:
resp = ApiResponse()
resp.error = ex
resp.status_code = 0 # with this status_code, content returns None
resp.request = Request(method, url).prepare()
return resp