httprunner3原始碼解讀(3)client.py

Silent丿丶黑羽發表於2021-11-05

原始碼目錄結構


 

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_headersrequest_cookiesrequest_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_headerscontent_typeresponse_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

相關文章