2011-11-03 22 views
6

我已经在Python 2.7中实现了Pivotal Tracker API模块。 Pivotal Tracker API预计POST数据是XML文档,“application/xml”是内容类型。当内容类型为“application/xml”时,如何使用httplib发布非ASCII字符

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

回答

7

你在混合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文本可以独立编码(它只是从消息的角度来看是一个八位字节流)。

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

使用ASCII编码对于self.url(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

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

+0

也许你可以改变头文件的内容类型,但是如何解决这个问题呢? 'msg'在python库中构造,并且是字节字符串。 – jro

+1

@jro:它与HTTP无关。看看上面的* complete *例子。 – jfs

+0

我知道这导致了这个问题,但我的观点是他无法控制'msg'变量。我同意你的观点,但是我的问题更多的是这个事实如何帮助他在libs中创建'msg'作为msg =“\ r \ n”.join(self._buffer) '? – jro

2

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

你可能会迫使编码self.url为unicode,然后httplib的会将所有的数据为Unicode

0

有三件事在这里介绍

  • 非Unicode字符串+ Unicode字符串,结果会自动转换为Unicode字符串。
  • Python 2.7 httplib,只是简单地用+加入头部与body,我认为这不是一个好习惯,我们不应该相信自动类型转换。但Python 2.6 httplib是不同的。
  • HTTP协议标准建议ISO-8859-1编码标题,但如果你想要把非ISO-8859-1字符,必须对其进行编码作为rfc2047描述

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

1

同JF塞巴斯蒂安的答案,但我加入一个新的,因此代码格式作品(和更谷歌能)

这里的,如果你想上的结束标记会发生什么一个机械化的表单请求:

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() 
相关问题