繁体   English   中英

当content-type为“application / xml”时,如何使用httplib发布非ASCII字符

[英]How do I post non-ASCII characters using httplib when content-type is “application/xml”

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

我的代码使用urlib / 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有效负载的自定义生产者)?

你正在混合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中的所有键,值应为bytestrings:

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类型; 将其转换为bytestring(使用'ascii'字符编码)。


HTTP消息由起始行,“标题”,空行和可能的消息体组成,因此self.headers用于标题, self.url用于起始行(http方法在这里),可能用于Host http标头(如果客户端是http / 1.1),XML文本转到消息体(作为二进制blob)。

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

以下是rfc 7230关于http 字符编码的说法

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

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

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

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

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

与JF Sebastian回答相同,但我添加了一个新代码,因此代码格式化工作(并且更具谷歌能力)

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

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()

这里有三件事要做

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

简单的解决方案是在发送之前严格地将头部和主体编码为utf-8。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM