日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[转]httpclient编码

發布時間:2024/9/21 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]httpclient编码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這幾天都在糾結Java Web開發中的中文編碼問題。其實,很多Java Web開發者都被中文編碼“折磨”過,網絡上有大量的討論。以前我也讀過這方面的博文,讀完后感覺似乎懂了,好像知道了編碼問題的原因和解決方法。但是, 一旦投入到實際開發中,發現自己其實沒懂,囧!
??? 連續糾結了幾天,總算對前因后果有個清晰地認識,故“略談”一下。之所以略談,是因為我并非(也沒有能力)完整地闡述Java Web開發的中文編碼問題,而是就事論事地總結這幾天遇到的問題和收獲。

問題?

??? 使用HttpClient 3.x發送GET或POST請求,請求參數中包含中文。服務器是Tocmat 5.5,通過斷點調試,發現Servlet拿到的中文參數是亂碼。顯然,HttpClient和Tomcat沒有就中文參數的編碼達成一致。
??? 于是,開始深入HttpClient和Tomcat的代碼,結合斷點調試,發現中文編碼問題并不是想象中的那么簡單。

術語約定?

??? 為了使得描述更加請求,我對本文中出現的“術語”進行約定,避免一詞多義引起的歧義。

  • Encoding: 編碼(動詞)
  • Decoding: 解碼(動詞)
  • Charset: 編碼或解碼使用的字符集
  • ??? 另外,編碼了的數據必然需要解碼,因此encoding和decoding往往是同現的。不過為了敘述簡練,下文需要兩者同現的地方,僅使用encoding。

    哪些數據需要encoding??

    ??? 在研究中文編碼問題前,我們首先要弄清一個問題:哪些數據需要encoding?

    ??? 一個Http請求的數據大致包括URI、Header、和Body三個部分。這三個部分貌似都需要encoding,不過我這次只涉及到URI和Body,因此 就不討論Header了。

    ??? 我們一般關心請求參數的中文編碼問題。雖然URI Path中也可以包括中文,但是。。。這不是給自己找麻煩嗎?
    ??? GET的請求參數在QueryString中,是URI的一部分。因此,對于GET請求,我們需要關注,URI是如何encoding的?
    ??? POST的請求參數在Body中,因此,對于POST請求,我們則需要關注,Body是如何encoding的?

    ??? 對于HttpClient和Tomcat來說,encoding和decoding本身是很容易的事情,關鍵是要知道charset是什么?要不通過API進行設置,要不通過配置文件進行配置。麻煩的是,URI和Body的charset還可以不一樣,使用不同的方法進行設置和配置。

    ??? HttpClient是一個類庫,通過自身提供的API對URI和Body的charset進行設置;Tomcat通過配置項和Servlet API,對URI和Body的charset進行設置。

    HttpClient如何設置charset??

    ??? 我們先看看如何設置GET請求QueryString的charset,然后看看POST請求Body的charset,最后看看如何獲取響應數據的charset。

    ????設置GET請求QueryString的charset

    ??? 我們通過GETMethod的setQueryString方法設置QueryString。setQueryString方法有兩種原型,我們分別看看。???

    [java]?view plaincopy
  • public?void?setQueryString(NameValuePair[]?params){??
  • ????LOG.trace("enter?HttpMethodBase.setQueryString(NameValuePair[])");??
  • ????queryString?=?EncodingUtil.formUrlEncode(params,?"UTF-8");??
  • }??


  • ??? 原型一以參數鍵值對的形式設置QueryString,使用固定的UTF-8作為charset,而且做URLEncode。因此,調用原型一之后,HttpClient就不會對QueryString再做任何encoding了。

    ??? 如果不想使用UTF-8,那么可以使用原型二。

    ???

    [java]?view plaincopy
  • public?void?setQueryString(String?queryString){??
  • ?????this.queryString?=?queryString;??
  • }??
  • ?
    ??? 原型二直接設置QueryString的內容。需要注意的是,queryString參數一定是按照某種charset進行URLEncode之后的字符串?。

    ??? 另外,也可以通過GETMethod的構造函數,直接設置URLEncode之后的uri?(包括了QueryString):

    ???

    [java]?view plaincopy
  • public?GetMethod(String?uri)?{??
  • ????super(uri);??
  • ????LOG.trace("enter?GetMethod(String)");??
  • ????setFollowRedirects(true);??
  • }??


  • ??? 設置POST請求Body的charset
    ?

    ??? 首先,我們可以在POST請求中的Header中設置Content-Type:

    ???

    [java]?view plaincopy
  • PostMethod?method?=?new?PostMethod();??
  • method.addRequestHeader("Content-Type","text/html;charset=UTF-8");??


  • ??? 在這里,Body的charset就UTF-8。

    ??? 其次,如果沒有設置Content-Type,我們還可以設置HttpClientParam的ContentCharset:
    ????

    [java]?view plaincopy
  • HttpClient?httpClient?=?new?HttpClient();??
  • HttpClientParam?params?=?httpClient.getParams();??
  • params.setContentCharset("UTF-8");??
  • ??
    ??? 然后,如果沒有設置HttpMethodParams的ContentCharset,我們還可以設置HttpMethodParams的ContentCharset:
    ???

    [java]?view plaincopy
  • PostMethod?method?=?new?PostMethod();??
  • HttpMethodParams?params?=?method.getParams();??
  • params.setContentCharset("UTF-8");??

  • ??? 這三種設置方法的優先級依次遞增,也就是說如果同時設置,則以后面的為準。如果都沒有設置,默認charset是ISO-8859-1。

    ????響應數據的charset?

    ??? 我們一般使用HttpMethodBase(GETMethod和PostMethod的父類)的getResponseBody系列方法獲取響應數據。getResponseBody系列方法包括:
    ???

    [java]?view plaincopy
  • public?byte[]?getResponseBody()?throws?IOException{...}??
  • public?byte[]?getResponseBody(int?maxlen)?throws?IOException{...}??
  • Public?InputStream?getResponseBodyAsStream()?throws?IOException?{...}??
  • public?String?getResponseBodyAsString()?throws?IOException?{...}??
  • public?String?getResponseBodyAsString(int?maxlen)?throws?IOException?{...}??
  • ?
    ??? 我比較喜歡getResponseBodyAsString方法,因為返回值類型是String,直接可以使用。不過,提到String就必須想到charset?。響應數據的charset肯定由Web Server(Tomcat)設置的,HttpMethodBase是怎么知道的呢?

    ??? 我們看看getResponseBodyAsString()方法的代碼:
    ???

    [java]?view plaincopy
  • public?String?getResponseBodyAsString()?throws?IOException?{??
  • ????byte[]?rawdata?=?null;??
  • ????if?(responseAvailable())?{??
  • ????????rawdata?=?getResponseBody();??
  • ????}??
  • ????if?(rawdata?!=?null)?{??
  • ????????return?EncodingUtil.getString(rawdata,?getResponseCharSet());??
  • ????}?else?{??
  • ????????return?null;??
  • ????}??
  • }??
  • ?
    ??? 顧名思義,getResponseCharSet方法的功能就是獲取響應數據的charset。那就看看她的代碼吧:
    ???

    [java]?view plaincopy
  • public?String?getResponseCharSet()?{??
  • return?getContentCharSet(getResponseHeader("Content-Type"));??
  • }??

  • ??? 可見,getResponseCharSet方法Content-Type Header獲取響應數據的charset。這要求Servlet必須正確設置response的Content-Type Header?。

    Tomcat如何設置charset??

    ??? 即使HttpClient正確設置了charset,Tomcat還要知道charset是什么,才能正確decoding。我們先看看如何設置GET請求QueryString的charset,然后看看POST請求Body的charset,最后看看Servlet響應數據的charset。

    ????設置GET請求QueryString的charset?

    ??? Tomcat通過URI的charset來設置QueryString的charset。我們可以在Tomcat根目錄下conf/server.xml?中進行配置。
    ???

    [xhtml]?view plaincopy
  • <Connector???
  • URIEncoding="UTF-8"???
  • useBodyEncodingForURI="true"????
  • acceptCount="100"???
  • connectionTimeout="20000"???
  • disableUploadTimeout="true"???
  • enableLookups="false"???
  • maxHttpHeaderSize="8192"???
  • maxSpareThreads="75"???
  • maxThreads="150"???
  • minSpareThreads="25"???
  • port="8080"???
  • redirectPort="8443"/>??
  • ??? URIEncoding屬性就是URI的charset,上述配置表示 Tomcat認為URI的charset就是UTF-8。如果HttpClient也使用UTF-8作為QueryString的charset,那么 Tomcat就可以正確decoding。詳情可以參考org.apache.tomcat.util.http.Parameters類的handleQueryParameters的方法:

    ???

    [java]?view plaincopy
  • //?--------------------?Processing?--------------------??
  • /**?Process?the?query?string?into?parameters?
  • ?*/??
  • public?void?handleQueryParameters()?{??
  • ????//?省略部分代碼??
  • ????processParameters(?decodedQuery,?queryStringEncoding?);??
  • }??

  • ??? Tomcat在啟動的過程中,如果從conf/server.xml中讀取到URIEncoding屬性,就會設置queryStringEncoding的值。當Tomcat處理HTTP請求時,上述方法就會被調用。

    ????默認的server.xml是沒有配置URIEncoding屬性的,需要我們手動設置?。如果沒有設置,Tomcat就會采用一種稱為“fast conversion”的方式解析QueryString。詳情可以參考org.apache.tomcat.util.http.Parameter類的urlDecode方法。

    ??? useBodyEncodingForURI是與URI charset相關的另一個屬性。如果該屬性的值為true,則Tomcat將使用Body的charset作為URI的charset。下一節將介紹Tomcat如何設置Body的charset。如果Tomcat沒有設置Body的charset,那么將使用HTTP請求Content-Type Header中的charset。如果HTTP請求中沒有設置Content-Type Header,則使用ISO-8859-1作為默認charset。詳情參見org.apache.catalina.connector.Request的parseParmeters方法:

    ???

    [java]?view plaincopy
  • /**?
  • ?*?Parse?request?parameters.?
  • ?*/??
  • protected?void?parseParameters()?{??
  • ????//?省略部分代碼??
  • ????String?enc?=?getCharacterEncoding();??
  • ????boolean?useBodyEncodingForURI?=?connector.getUseBodyEncodingForURI();??
  • ????if?(enc?!=?null)?{??
  • ????????parameters.setEncoding(enc);??
  • ????????if?(useBodyEncodingForURI)?{??
  • ????????????parameters.setQueryStringEncoding(enc);??
  • ????????}??
  • ????}?else?{??
  • ????????parameters.setEncoding?(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);??
  • ????????if?(useBodyEncodingForURI)?{??
  • ????????????parameters.setQueryStringEncoding?(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);??
  • ????????}??
  • ????}??
  • ????parameters.handleQueryParameters();??
  • ????//?省略部分代碼??
  • }??

  • ?????默認的server.xml是沒有配置useBodyEncodingForURI屬性的,需要我們手動設置?。如果沒有設置,Tomcat則認為其值為false。需要注意的是,如果URIEncoding和useBodyEncodingForURI同時設置,而且Body的charset已經設置,那么將以Body的charset為準?。

    ????設置POST請求Body的charset?

    ??? 設置Body charset的方法很簡單,只要調用javax.servlet.ServletRequest接口的setCharacterEncoding方法即可,比如request.setCharacterEncoding("UTF-8")。需要注意的是,該方法必須在讀取任何請求參數之前調用,才有效果。詳情可以參見該方法的注釋:
    ????

    [java]?view plaincopy
  • /**?
  • ?*?Overrides?the?name?of?the?character?encoding?used?in?the?body?of?this?
  • ?*?request.?This?method?must?be?called?prior?to?reading?request?parameters?
  • ?*?or?reading?input?using?getReader().?
  • ?*?
  • ?*?
  • ?*?@param?env?a?<code>String</code>?containing?the?name?of?
  • ?*?the?character?encoding.?
  • ?*?@throws?java.io.UnsupportedEncodingException?if?this?is?not?a?valid?encoding?
  • ?*/??
  • public?void?setCharacterEncoding(String?env)?throws?java.io.UnsupportedEncodingException;??


  • ???? 也就是說,我們只有在調用getParameter或getReader方法之前,調用setsetCharacterEncoding方法,設置的charset才能奏效。

    ????響應數據的charset

    ??? 設置響應數據charset的方法很簡單,只要調用javax.servlet.ServletResponse接口的setContentType或setCharacterEncoding方法即可,比如response.setContentType("text/html;charset=UTF-8")或response.setCharacterEncoding("UTF-8")。需要注意的是,這兩個方法的調用時機也是有講究的,詳情可以參見他們的注釋。

    ?

    ??? 如果Servlet正確設置了響應數據的charset,那么HTTP響應數據中就會包含Content-Type Header。HttpClient的getResponseBodyAsString方法就可以正確decoding響應數據。



    總結?

    ??? 在開發Java Web應用的過程中,遇到中文亂碼問題,應該是一件正常的事情。我們不必首先懷疑HtpClient或Tomcat有莫名奇妙的bug,往往都是我們使用不當或配置不當。凡事總有原因,總要相信科學嘛!如果想徹底了解中文編碼的前因后果,我覺得可以從HTTP規范、Servlet規范、HttpClient的API文檔和Tomcat的配置文檔入手,必要時可以追蹤HttpClient和Tomcat的代碼。

    總結

    以上是生活随笔為你收集整理的[转]httpclient编码的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。