这个类没啥好说的
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)
这个函数的功能是获取请求记录和响应记录,源码分为4段来看
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格式
# 记录实际请求信息(请求头、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
模型中,最后打印请求的信息
# 记录响应信息 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
模型类中,最后打印响应日志
req_resp_data = ReqRespData(request=request_data, response=response_data) return req_resp_data
最后这段就是将刚才的请求信息和响应信息全部放入ReqRespData
模型中,最后get_req_resp_record
函数返回的内容就是ReqRespData
模型
在requests.Session
上进行了二次封装,该类包含4个方法,下面依次介绍
def __init__(self): super(HttpSession, self).__init__() self.data = SessionData()
初始化方法,定义了data属性的默认值为SessionData
模型,该模型包含了req_resps: List[ReqRespData] = []
请求响应内容
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
列表中
发送requests.Request
请求,返回requests.Response
响应,还做了以下事情
发送一个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