2011-11-03 45 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() 
相關問題