Google Drive Python SDK遇SSL错误:React请求异常Postman正常
排查Flask + Google Drive API在React请求时的SSL记录层错误
问题背景
我在Flask应用中实现了一个方法,用于在Google Drive中查找指定文件夹,不存在则创建并返回folder_id:
def find_or_create_folder_in_google_drive(name, parent_id=None): query = f"name='{name}'" if parent_id: query += f" and '{parent_id}' in parents" query += " and mimeType='application/vnd.google-apps.folder'" query += " and trashed=false" response = ( GOOGLE_DRIVE_SERVICE.files() .list(q=query, spaces="drive", fields="files(id, name)") .execute() ) files = response.get("files", []) if not files: file_metadata = {"name": name, "mimeType": "application/vnd.google-apps.folder"} if parent_id: file_metadata["parents"] = [parent_id] folder = ( GOOGLE_DRIVE_SERVICE.files() .create(body=file_metadata, fields="id") .execute() ) return folder.get("id") return files[0].get("id")
该代码在Postman、Thunder Client等API测试工具中运行正常,但通过React前端发送相同请求时,抛出如下SSL异常:
File "path/backend/storage_manager/google/prompts.py", line 19, in create_or_get_prompts_path_google_sheets root_folder_id = find_or_create_folder_in_google_drive(ROOT_FOLDER) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "path/backend/storage_manager/google_services/get_drive_paths.py", line 15, in find_or_create_folder_in_google_drive .execute() ^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper return wrapped(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/googleapiclient/http.py", line 923, in execute resp, content = _retry_request( ^^^^^^^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/googleapiclient/http.py", line 222, in _retry_request raise exception File "path/backend/.venv/lib/python3.11/site-packages/googleapiclient/http.py", line 191, in _retry_request resp, content = http.request(uri, method, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/google_auth_httplib2.py", line 218, in request response, content = self.http.request( ^^^^^^^^^^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/httplib2/__init__.py", line 1724, in request (response, content) = self._request( ^^^^^^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/httplib2/__init__.py", line 1444, in _request (response, content) = self._conn_request(conn, request_uri, method, body, headers) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "path/backend/.venv/lib/python3.11/site-packages/httplib2/__init__.py", line 1396, in _conn_request response = conn.getresponse() ^^^^^^^^^^^^^^^^^^ File "/usr/local/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/http/client.py", line 1378, in getresponse response.begin() File "/usr/local/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/http/client.py", line 318, in begin version, status, reason = self._read_status() ^^^^^^^^^^^^^^^^^^^ File "/usr/local/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/http/client.py", line 279, in _read_status line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/socket.py", line 706, in readinto return self._sock.recv_into(b) ^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/ssl.py", line 1278, in recv_into return self.read(nbytes, buffer) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/ssl.py", line 1134, in read return self._sslobj.read(len, buffer) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ssl.SSLError: [SSL] record layer failure (_ssl.c:2576)
错误触发点为GOOGLE_DRIVE_SERVICE.files().list(...).execute()这一行。当前使用的Google依赖版本:
google-api-core==2.16.1 google-api-python-client==2.116.0 google-auth==2.27.0 google-auth-httplib2==0.2.0 google-auth-oauthlib==1.2.0 googleapis-common-protos==1.62.0
排查思路
- 排除CORS直接关联:CORS错误会在浏览器控制台显示,后端SSL异常是与Google Drive API通信时的问题,和前端跨域本身无关,但前端请求的特性可能间接触发。
- 对比请求差异:检查Postman与React请求的请求头、参数编码、请求方式是否存在差异,比如React是否携带了特殊头导致后端处理逻辑变化。
- 网络环境排查:Postman可能使用系统代理,而React前端所在环境(浏览器/开发服务器)使用不同代理,导致后端访问Google API的网络路径变化,触发SSL握手问题。
- 依赖兼容性检查:验证httplib2与Python 3.11的兼容性,或Google客户端库之间的版本冲突。
- 连接稳定性排查:React请求可能触发后端并发处理,导致Google API请求的连接被中断,引发SSL记录层错误。
解决方案
1. 替换httplib2为requests适配器
httplib2在部分环境下易出现SSL问题,改用基于requests的适配器提升稳定性:
from googleapiclient.discovery import build from google_auth_httplib2 import AuthorizedHttp import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry # 创建带重试机制的requests会话 session = requests.Session() retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504] ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("https://", adapter) # 替换Google服务的HTTP客户端 authorized_http = AuthorizedHttp(credentials, http=session) GOOGLE_DRIVE_SERVICE = build('drive', 'v3', http=authorized_http)
2. 临时调整SSL配置(仅测试用)
临时禁用证书验证排查是否为证书问题(生产环境禁止使用):
import httplib2 httplib2.disable_ssl_certificate_validation = True
或指定SSL版本:
import ssl from httplib2 import Http http = Http() http.ca_certs = "/path/to/ca-certificates.crt" http.ssl_version = ssl.PROTOCOL_TLSv1_2
3. 修复Flask CORS配置
虽然不是直接原因,但确保CORS配置正确,避免前端请求被拦截后引发后端异常:
from flask_cors import CORS app = Flask(__name__) # 生产环境请指定具体域名,而非允许所有 CORS(app)
4. 更新依赖库
尝试更新Google客户端库和httplib2到最新版本,修复兼容性问题:
pip install --upgrade google-api-python-client google-auth httplib2
5. 添加请求重试机制
在Google API请求中增加重试逻辑,处理临时网络中断:
import ssl import time from googleapiclient.errors import HttpError def find_or_create_folder_in_google_drive(name, parent_id=None): query = f"name='{name}'" if parent_id: query += f" and '{parent_id}' in parents" query += " and mimeType='application/vnd.google-apps.folder' and trashed=false" max_retries = 3 for attempt in range(max_retries): try: response = ( GOOGLE_DRIVE_SERVICE.files() .list(q=query, spaces="drive", fields="files(id, name)") .execute() ) break except ssl.SSLError as e: if attempt == max_retries - 1: raise e time.sleep(2 ** attempt) # 指数退避等待 files = response.get("files", []) # 后续创建逻辑保持不变...
内容的提问来源于stack exchange,提问作者Malice
相关产品推荐
相关产品推荐

