如何从Python2.7 urllib2.Request迁移至Python3.10并兼容编码数据?
Python2 到 Python3 POST 请求迁移的 500 错误解决
问题背景
正在将 Python2.7 中使用 urllib2.Request 的 POST 请求代码迁移到 Python3.10,改用新版 json.dumps() 和 urllib.request.Request,但服务器返回 500 内部错误。核心需求是将包含 PNG 图片字节的字典发送到本地 Web 服务,原 Python2 代码可正常运行,新代码无法被服务器接受。
Python2.7 可用代码
def post(self, endpoint, data={}, params={}, headers=None): try: url = self.base_url + endpoint + "&" + urllib.urlencode(params) data = json.dumps(data, False) # which parameter gets the False value? headers = headers or {"Content-Type": "application/json", "Accept": "application/json"} request = urllib2.Request(url=url, data=data, headers=headers) response = urllib2.urlopen(request) data = response.read() data = json.loads(data) return data except Exception as ex: logging.exception("ERROR: ApiClient.post")
有问题的 Python3.10 代码
class BytesEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, bytes): return base64.b64encode(o).decode('utf-8') else: return super().default(o) def post(self, endpoint, dict_in={}, params={}, headers=None): try: url = self.base_url + endpoint + "&" + urllib.parse.urlencode(params) # NOTE: In the original code, the string returned by json.dumps is sent directly to urllib2.Request. # That changes in urllib.request.Request, where the data parameter specifies "bytes" # Original line was "data = json.dumps(data, False)" json_str: str = json.dumps(dict_in, sort_keys=True, indent=2, cls=BytesEncoder) json_bytes: bytes = json_str.encode('utf-8') headers = headers or {"Content-Type": "application/json", "Accept": "application/json"} request_out = urllib.request.Request(url=url, data=json_bytes, headers=headers) """ FIXME: The following urlopen raises HTTPError url=http://127.0.0.1:7860/sdapi/v1/img2img?, code=500, reason=Internal Server Error #/components/schemas/StableDiffusionProcessingImg2Img """ response = urlopen(request_out) response_json_str = response.read() response_dict = json.loads(response_json_str) return response_dict except urllib.error.HTTPError as ex: StabDiffAuto1111.LOGGER.exception("ERROR: ApiClient.post()") message = "url=%s, code=%d, reason=%s" % (ex.url, ex.code, ex.reason) StabDiffAuto1111.LOGGER.error(message) StabDiffAuto1111.LOGGER.error(str(ex.headers))
用户指出的核心差异:
- 新增了自定义
BytesEncoder对 bytes 做 base64 编码,原 Python2 代码无此处理 - 给 JSON 序列化添加了
sort_keys=True和indent=2,改变了输出格式 - 将 JSON 字符串编码为 UTF-8 字节,怀疑此步骤导致服务器不兼容
解决步骤与修正代码
关键修正点
- 对齐
json.dumps参数:原 Python2 代码中json.dumps(data, False)等价于设置ensure_ascii=False,Python3 中需显式声明该参数,保持序列化行为一致。 - 还原 bytes 处理逻辑:Python2 中 bytes 和 str 混同,直接将字节序列化为字符串;Python3 中需用
latin-1解码 bytes(单字节编码,无损转换所有字节值),模拟原行为,无需 base64 编码。 - 移除格式修改参数:去掉
sort_keys=True和indent=2,保持原代码的紧凑 JSON 输出格式。 - 修复 URL 参数拼接:避免手动拼接
&导致的格式错误,用urlparse和urlencode正确合并已有参数与新增参数。
最终修正后的 Python3 代码
def post(self, endpoint, dict_in={}, params={}, headers=None): try: # 正确拼接 URL 参数,避免 ? 和 & 的格式问题 from urllib.parse import urlparse, parse_qsl, urlencode parsed_url = urlparse(self.base_url + endpoint) existing_params = dict(parse_qsl(parsed_url.query)) existing_params.update(params) new_query = urlencode(existing_params) url = parsed_url._replace(query=new_query).geturl() # 将字典中的 bytes 转换为字符串,模拟 Python2 的行为 def convert_bytes(data): if isinstance(data, dict): return {k: convert_bytes(v) for k, v in data.items()} elif isinstance(data, bytes): return data.decode('latin-1') # 单字节编码,无损转换所有字节值 return data processed_data = convert_bytes(dict_in) # 和 Python2 的 json.dumps(data, False) 对齐:ensure_ascii=False,无缩进、不排序键 json_str = json.dumps(processed_data, ensure_ascii=False) json_bytes = json_str.encode('utf-8') headers = headers or {"Content-Type": "application/json", "Accept": "application/json"} request_out = urllib.request.Request(url=url, data=json_bytes, headers=headers) response = urllib.request.urlopen(request_out) response_json_str = response.read() response_dict = json.loads(response_json_str) return response_dict except urllib.error.HTTPError as ex: StabDiffAuto1111.LOGGER.exception("ERROR: ApiClient.post()") message = "url=%s, code=%d, reason=%s" % (ex.url, ex.code, ex.reason) StabDiffAuto1111.LOGGER.error(message) StabDiffAuto1111.LOGGER.error(str(ex.headers))
内容的提问来源于 stack exchange,提问作者 Charlweed
相关产品推荐
相关产品推荐

