http_client.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import requests
  2. import logging
  3. import os
  4. import json
  5. from typing import Dict, Any, Optional
  6. from urllib3.util.retry import Retry
  7. from requests.adapters import HTTPAdapter
  8. # 配置日志
  9. logging.basicConfig(level=logging.INFO)
  10. logger = logging.getLogger(__name__)
  11. class HTTPClient:
  12. """HTTP请求工具类,用于发送各种HTTP请求"""
  13. def __init__(self, base_url: str, api_key: str = None, auth_type: str = 'bearer'):
  14. """
  15. 初始化HTTP客户端
  16. Args:
  17. base_url: API基础URL
  18. api_key: API密钥
  19. auth_type: 认证类型,支持'bearer'和'basic'
  20. """
  21. self.base_url = base_url.rstrip('/')
  22. self.api_key = api_key
  23. self.session = requests.Session()
  24. # 设置请求超时(秒)
  25. self.timeout = 30
  26. # 配置重试机制
  27. retry_strategy = Retry(
  28. total=3, # 最大重试次数
  29. backoff_factor=1, # 重试间隔:1秒、2秒、4秒
  30. status_forcelist=[502, 503, 504], # 重试的状态码(移除500,避免过多无效重试)
  31. allowed_methods=["GET", "POST", "PUT", "DELETE"] # 允许重试的方法
  32. )
  33. # 创建HTTP适配器并设置重试策略
  34. adapter = HTTPAdapter(max_retries=retry_strategy)
  35. # 将适配器应用到所有请求
  36. self.session.mount("http://", adapter)
  37. self.session.mount("https://", adapter)
  38. # 设置默认请求头
  39. if self.api_key:
  40. if auth_type == 'bearer':
  41. self.session.headers.update({
  42. 'Authorization': f'Bearer {self.api_key}'
  43. })
  44. elif auth_type == 'basic':
  45. # 处理Basic Auth,格式为"username:password"
  46. self.session.headers.update({
  47. 'Authorization': f'Basic {self.api_key}'
  48. })
  49. self.session.headers.update({
  50. 'Content-Type': 'application/json'
  51. })
  52. def post(self, endpoint: str, data: Optional[Dict] = None,
  53. json_data: Optional[Dict] = None, files: Optional[Dict] = None,
  54. headers: Optional[Dict] = None) -> Dict[str, Any]:
  55. """
  56. 发送POST请求
  57. Args:
  58. endpoint: API端点路径(以/开头)
  59. data: 表单数据
  60. json_data: JSON数据
  61. files: 文件数据
  62. headers: 自定义请求头
  63. Returns:
  64. Dict: 响应JSON数据
  65. Raises:
  66. requests.exceptions.RequestException: 请求失败时抛出
  67. """
  68. url = f"{self.base_url}{endpoint}"
  69. # 记录请求日志
  70. request_info = {
  71. "method": "POST",
  72. "url": url,
  73. "data": data,
  74. "json": json_data,
  75. "headers": headers,
  76. "files": files is not None # 不记录文件内容,只记录是否有文件
  77. }
  78. # 将请求报文写入D:\project\work\ragflow_plugs\book\output\temp下的request.txt文件
  79. with open(r"D:\project\work\ragflow_plugs\book\output\temp\request.txt", "w", encoding="utf-8") as f:
  80. f.write(str(request_info))
  81. logger.info(f"Sending request: {request_info}")
  82. try:
  83. # 当上传文件时,不使用默认的Content-Type: application/json头
  84. # 让requests库自动生成正确的multipart/form-data头
  85. if files:
  86. # 创建一个临时会话,不包含默认的Content-Type头
  87. temp_session = requests.Session()
  88. # 复制认证头
  89. if self.api_key:
  90. auth_header = self.session.headers.get('Authorization')
  91. if auth_header:
  92. temp_session.headers.update({'Authorization': auth_header})
  93. # 使用临时会话发送请求
  94. response = temp_session.post(
  95. url=url,
  96. data=data,
  97. json=json_data,
  98. files=files,
  99. headers=headers,
  100. timeout=self.timeout # 添加超时参数
  101. )
  102. else:
  103. # 正常请求,使用默认会话
  104. response = self.session.post(
  105. url=url,
  106. data=data,
  107. json=json_data,
  108. headers=headers,
  109. timeout=self.timeout # 添加超时参数
  110. )
  111. # 记录响应日志
  112. response_info = {
  113. "status_code": response.status_code,
  114. "url": url,
  115. "headers": dict(response.headers),
  116. "content_length": len(response.content)
  117. }
  118. logger.info(f"Received response: {response_info}")
  119. response.raise_for_status() # 抛出HTTP错误
  120. return response.json()
  121. except Exception as e:
  122. # 记录错误日志
  123. logger.error(f"Request failed: {str(e)}")
  124. raise
  125. def get(self, endpoint: str, params: Optional[Dict] = None,
  126. headers: Optional[Dict] = None) -> Dict[str, Any]:
  127. """
  128. 发送GET请求
  129. Args:
  130. endpoint: API端点路径(以/开头)
  131. params: 查询参数
  132. headers: 自定义请求头
  133. Returns:
  134. Dict: 响应JSON数据
  135. Raises:
  136. requests.exceptions.RequestException: 请求失败时抛出
  137. """
  138. url = f"{self.base_url}{endpoint}"
  139. # 记录请求日志
  140. request_info = {
  141. "method": "GET",
  142. "url": url,
  143. "params": params,
  144. "headers": headers
  145. }
  146. logger.info(f"Sending request: {request_info}")
  147. try:
  148. response = self.session.get(
  149. url=url,
  150. params=params,
  151. headers=headers,
  152. timeout=self.timeout # 添加超时参数
  153. )
  154. # 记录响应日志
  155. response_info = {
  156. "status_code": response.status_code,
  157. "url": url,
  158. "headers": dict(response.headers),
  159. "content_length": len(response.content)
  160. }
  161. logger.info(f"Received response: {response_info}")
  162. response.raise_for_status() # 抛出HTTP错误
  163. return response.json()
  164. except Exception as e:
  165. # 记录错误日志
  166. logger.error(f"Request failed: {str(e)}")
  167. raise
  168. def get_json(self, endpoint: str, json_data: Optional[Dict] = None,
  169. headers: Optional[Dict] = None) -> Dict[str, Any]:
  170. """
  171. 发送带有JSON数据的GET请求
  172. Args:
  173. endpoint: API端点路径(以/开头)
  174. json_data: JSON数据
  175. headers: 自定义请求头
  176. Returns:
  177. Dict: 响应JSON数据
  178. Raises:
  179. requests.exceptions.RequestException: 请求失败时抛出
  180. """
  181. url = f"{self.base_url}{endpoint}"
  182. # 记录请求日志
  183. request_info = {
  184. "method": "GET",
  185. "url": url,
  186. "json": json_data,
  187. "headers": headers
  188. }
  189. logger.info(f"Sending request: {request_info}")
  190. try:
  191. response = self.session.get(
  192. url=url,
  193. json=json_data,
  194. headers=headers,
  195. timeout=self.timeout # 添加超时参数
  196. )
  197. # 记录响应日志
  198. response_info = {
  199. "status_code": response.status_code,
  200. "url": url,
  201. "headers": dict(response.headers),
  202. "content_length": len(response.content)
  203. }
  204. logger.info(f"Received response: {response_info}")
  205. response.raise_for_status() # 抛出HTTP错误
  206. # 将response.content转换为JSON
  207. return json.loads(response.content.decode('utf-8'))
  208. except Exception as e:
  209. # 记录错误日志
  210. logger.error(f"Request failed: {str(e)}")
  211. raise
  212. def put(self, endpoint: str, data: Optional[Dict] = None,
  213. json_data: Optional[Dict] = None, headers: Optional[Dict] = None) -> Dict[str, Any]:
  214. """
  215. 发送PUT请求
  216. Args:
  217. endpoint: API端点路径(以/开头)
  218. data: 表单数据
  219. json_data: JSON数据
  220. headers: 自定义请求头
  221. Returns:
  222. Dict: 响应JSON数据
  223. Raises:
  224. requests.exceptions.RequestException: 请求失败时抛出
  225. """
  226. url = f"{self.base_url}{endpoint}"
  227. # 记录请求日志
  228. request_info = {
  229. "method": "PUT",
  230. "url": url,
  231. "data": data,
  232. "json": json_data,
  233. "headers": headers
  234. }
  235. logger.info(f"Sending request: {request_info}")
  236. try:
  237. response = self.session.put(
  238. url=url,
  239. data=data,
  240. json=json_data,
  241. headers=headers,
  242. timeout=self.timeout # 添加超时参数
  243. )
  244. # 记录响应日志
  245. response_info = {
  246. "status_code": response.status_code,
  247. "url": url,
  248. "headers": dict(response.headers),
  249. "content_length": len(response.content)
  250. }
  251. logger.info(f"Received response: {response_info}")
  252. response.raise_for_status() # 抛出HTTP错误
  253. return response.json()
  254. except Exception as e:
  255. # 记录错误日志
  256. logger.error(f"Request failed: {str(e)}")
  257. raise
  258. def delete(self, endpoint: str, data: Optional[Dict] = None,
  259. json_data: Optional[Dict] = None, headers: Optional[Dict] = None) -> Dict[str, Any]:
  260. """
  261. 发送DELETE请求
  262. Args:
  263. endpoint: API端点路径(以/开头)
  264. data: 表单数据
  265. json_data: JSON数据
  266. headers: 自定义请求头
  267. Returns:
  268. Dict: 响应JSON数据
  269. Raises:
  270. requests.exceptions.RequestException: 请求失败时抛出
  271. """
  272. url = f"{self.base_url}{endpoint}"
  273. # 记录请求日志
  274. request_info = {
  275. "method": "DELETE",
  276. "url": url,
  277. "data": data,
  278. "json": json_data,
  279. "headers": headers
  280. }
  281. logger.info(f"Sending request: {request_info}")
  282. try:
  283. response = self.session.delete(
  284. url=url,
  285. data=data,
  286. json=json_data,
  287. headers=headers,
  288. timeout=self.timeout # 添加超时参数
  289. )
  290. # 记录响应日志
  291. response_info = {
  292. "status_code": response.status_code,
  293. "url": url,
  294. "headers": dict(response.headers),
  295. "content_length": len(response.content)
  296. }
  297. logger.info(f"Received response: {response_info}")
  298. response.raise_for_status() # 抛出HTTP错误
  299. return response.json()
  300. except Exception as e:
  301. # 记录错误日志
  302. logger.error(f"Request failed: {str(e)}")
  303. raise
  304. def upload_file(self, endpoint: str, file_path: str, file_field_name: str = 'file',
  305. data: Optional[Dict] = None, headers: Optional[Dict] = None) -> Dict[str, Any]:
  306. """
  307. 上传文件
  308. Args:
  309. endpoint: API端点路径(以/开头)
  310. file_path: 本地文件路径
  311. file_field_name: 表单字段名称
  312. data: 额外的表单数据
  313. headers: 自定义请求头
  314. Returns:
  315. Dict: 响应JSON数据
  316. Raises:
  317. requests.exceptions.RequestException: 请求失败时抛出
  318. """
  319. # 打开文件并构建files字典
  320. with open(file_path, 'rb') as f:
  321. files = {
  322. file_field_name: (os.path.basename(file_path), f)
  323. }
  324. # 发送POST请求
  325. return self.post(endpoint, data=data, files=files, headers=headers)