【问题标题】:How do I post non-ASCII characters using httplib when content-type is "application/xml"当内容类型为“application/xml”时,如何使用 httplib 发布非 ASCII 字符
【发布时间】:2023-03-28 14:17:02
【问题描述】:

我在 Python 2.7 中实现了一个 Pivotal Tracker API 模块。 Pivotal Tracker API 期望 POST 数据是 XML 文档,“application/xml”是内容类型。

我的代码使用urllib/httplib来发布文档如图:

    request = urllib2.Request(self.url, xml_request.toxml('utf-8') if xml_request else None, self.headers)
    obj = parse_xml(self.opener.open(request))

当 XML 文本包含非 ASCII 字符时,这会产生异常:

File "/usr/lib/python2.7/httplib.py", line 951, in endheaders
  self._send_output(message_body)
File "/usr/lib/python2.7/httplib.py", line 809, in _send_output
  msg += message_body
exceptions.UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 89: ordinal not in range(128)

据我所见,httplib._send_output 正在为消息负载创建一个 ASCII 字符串,大概是因为它希望数据是 URL 编码的(application/x-www-form-urlencoded)。只要只使用 ASCII 字符,它就可以与 application/xml 一起使用。

是否有一种直接的方法来发布包含非 ASCII 字符的应用程序/xml 数据,或者我是否必须跳过障碍(例如,使用 Twistd 和 POST 有效负载的自定义生产者)?

【问题讨论】:

    标签: python django urllib2 httplib pivotaltracker


    【解决方案1】:

    您正在混合使用 Unicode 和字节串。

    >>> msg = u'abc' # Unicode string
    >>> message_body = b'\xc5' # bytestring
    >>> msg += message_body
    Traceback (most recent call last):
      File "<input>", line 1, in <module>
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 0: ordinal \
    not in range(128)
    

    要修复它,请确保 self.headers 内容已正确编码,即 headers 中的所有键、值应为字节串:

    self.headers = dict((k.encode('ascii') if isinstance(k, unicode) else k,
                         v.encode('ascii') if isinstance(v, unicode) else v)
                        for k,v in self.headers.items())
    

    注意:标头的字符编码与正文的字符编码无关,即 xml 文本可以独立编码(从 http 消息的角度来看,它只是一个八位字节流)。

    self.url 也是如此——如果它具有 unicode 类型;将其转换为字节串(使用 'ascii' 字符编码)。


    HTTP message consists of a start-line, "headers", an empty line and possibly a message-body 所以self.headers 用于标题,self.url 用于起始行(http 方法在这里)并且可能用于Host http 标题(如果客户端是 http/1.1),XML 文本去到消息正文(作为二进制 blob)。

    self.url 使用 ASCII 编码总是安全的(IDNA 可用于非 ascii 域名——结果也是 ASCII)。

    这是rfc 7230 says about http headers character encoding

    从历史上看,HTTP 允许在 ISO-8859-1 字符集 [ISO-8859-1],仅支持其他字符集 通过使用 [RFC2047] 编码。在实践中,大多数 HTTP 标头 字段值仅使用 US-ASCII 字符集 [USASCII] 的子集。 新定义的头域应该限制它们的域值 US-ASCII 八位字节。收件人应该处理字段中的其他八位位组 内容(obs-text)作为不透明数据。

    要将 XML 转换为字节串,请参阅application/xml encoding condsiderations

    建议所有 XML MIME 实体都使用 UTF-8,不带 BOM。

    【讨论】:

    • 也许您可以更改标题的content-type,但这如何解决问题? msg 在 python 库中构造,是字节字符串。
    • @jro:这与 HTTP 无关。查看上面的完整示例。
    • 我知道这会导致问题,但我的意思是他无法控制 msg 变量。我同意你的观点,但我的问题更多的是当在库中msg 被创建为msg = "\r\n".join(self._buffer) 时,这个事实如何帮助他解决它?
    • @jro:查看问题中的urllib2.Request(.. 行。有self.headers。我在答案中添加了代码,以确保它没有 Unicode 字符串。
    • 只需阅读其余的 cmets。澄清一下,消息是在 httplib 中从方法、URL、标头等构造的。如果其中任何一个是 Unicode,则整个字符串将转换为 Unicode(我认为这是正常的 Python 行为)。然后,如果您尝试附加 UTF-8 字符串,则会收到我在原始问题中描述的错误。
    【解决方案2】:

    检查self.url 是否为 unicode。如果是 unicode,则httplib 会将数据视为 unicode。

    您可以强制将 self.url 编码为 un​​icode,然后 httplib 会将所有数据视为 unicode

    【讨论】:

      【解决方案3】:

      与 JF Sebastian 的答案相同,但我添加了一个新答案,以便代码格式化工作(并且更适合 google)

      如果您尝试在机械化表单请求的末尾添加标签,会发生以下情况:

      br = mechanize.Browser()
      br.select_form(nr=0)
      br['form_thingy'] = u"Wonderful"
      headers = dict((k.encode('ascii') if isinstance(k, unicode) else k, v.encode('ascii') if isinstance(v, unicode) else v) for k,v in br.request.headers.items())
      br.addheaders = headers
      req = br.submit()
      

      【讨论】:

        【解决方案4】:

        这里要介绍三件事

        • 非Unicode字符串+Unicode字符串,结果会自动转成Unicode字符串。
        • Python 2.7 httplib,简单地使用 + 将 header 与 body 连接起来,我认为这不是一个好习惯,我们不应该相信自动类型转换。但 Python 2.6 httplib 不同。
        • HTTP 协议标准建议对标头进行 ISO-8859-1 编码,但如果要放入非 ISO-8859-1 字符,则必须将其编码为rfc2047 描述

        简单的解决方案是在发送之前将 header 和 body 都严格编码为 utf-8。

        【讨论】:

          猜你喜欢
          • 2011-04-06
          • 2017-07-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-26
          • 2016-01-29
          • 2019-03-21
          • 2014-01-06
          相关资源
          最近更新 更多