• httprunner3源码解读(3)client.py


    源码目录结构


     

    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"
    ================== {r_type} details ==================
    "
            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} : {}
    ".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
    
  • 相关阅读:
    vim编辑器
    linux常用的命令解释
    克隆虚拟机及本地仓库的搭建
    创建windows系统下的虚拟机
    创建linux系统下的虚拟机
    drf频率组件
    django中过滤 搜索 排序
    drf分页
    js回顾
    数据类型
  • 原文地址:https://www.cnblogs.com/jiakecong/p/15513479.html
Copyright © 2020-2023  润新知