You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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 字节,怀疑此步骤导致服务器不兼容

解决步骤与修正代码

关键修正点

  1. 对齐 json.dumps 参数:原 Python2 代码中 json.dumps(data, False) 等价于设置 ensure_ascii=False,Python3 中需显式声明该参数,保持序列化行为一致。
  2. 还原 bytes 处理逻辑:Python2 中 bytes 和 str 混同,直接将字节序列化为字符串;Python3 中需用 latin-1 解码 bytes(单字节编码,无损转换所有字节值),模拟原行为,无需 base64 编码。
  3. 移除格式修改参数:去掉 sort_keys=True 和 indent=2,保持原代码的紧凑 JSON 输出格式。
  4. 修复 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:32:03