搞清tomcat中的編解碼
經常會被亂碼問題攪得頭暈腦脹。事實上,亂碼問題涉及的地方比較多,所以常常有了問題也很難定位,比如,可以發生在容器,可以發生在MVC框架,可以發生在資料庫,可以發生在響應等等。
這裡分析一下tomcat中是如何編解碼的。
以"http://localhost:8080/測試?網路=程式設計"為例,可以將tomcat中編解碼分解為這麼幾個地方:
1. pathInfo.即“測試”這個部分
2. queryParameter,即”網路=程式設計“這個部分
3. http header,即瀏覽器傳送的http頭部分
4. requestBody,http正文部分,即post的正文部分
1. pathInfo,Http11Processor中的process方法會呼叫InternelInputBuffer來解析請求URL(inputBuffer.parseRequestLine)以及請求頭(inputBuffer.parseHeaders),但是這裡並不是解碼的地方。
public void process(Socket theSocket) throws IOException { ... inputBuffer.parseRequestLine(); request.setStartTime(System.currentTimeMillis()); keptAlive = true; if (disableUploadTimeout) { socket.setSoTimeout(soTimeout); } else { socket.setSoTimeout(timeout); } // Set this every time in case limit has been changed via JMX request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount()); inputBuffer.parseHeaders(); ... }
真正解碼的地方是CoyoteAdapter的convertURI
protected void convertURI(MessageBytes uri, Request request) throws Exception { ByteChunk bc = uri.getByteChunk(); int length = bc.getLength(); CharChunk cc = uri.getCharChunk(); cc.allocate(length, -1); String enc = connector.getURIEncoding(); if (enc != null) { B2CConverter conv = request.getURIConverter(); try { if (conv == null) { conv = new B2CConverter(enc); request.setURIConverter(conv); } } catch (IOException e) { // Ignore log.error("Invalid URI encoding; using HTTP default"); connector.setURIEncoding(null); } if (conv != null) { try { conv.convert(bc, cc); uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength()); return; } catch (IOException e) { log.error("Invalid URI character encoding; trying ascii"); cc.recycle(); } } } // Default encoding: fast conversion byte[] bbuf = bc.getBuffer(); char[] cbuf = cc.getBuffer(); int start = bc.getStart(); for (int i = 0; i < length; i++) { cbuf[i] = (char) (bbuf[i + start] & 0xff); } uri.setChars(cbuf, 0, length); }
而這裡的解碼使用的是connector的URIEncoding,所以pathInfo的解碼可以通過配置server.xml中的URIEncoding來改變。
2. queryParameter部分,這裡其實有幾個地方可以控制,首先,我們還是找到解碼queryParameter的地方。在呼叫request.getParameter時最終會呼叫到coyote內部的Parameter中的handleQueryParameters方法,可以看到這裡的queryStringEncoding。
public void handleQueryParameters() {
if( didQueryParameters ) return;
didQueryParameters=true;
if( queryMB==null || queryMB.isNull() )
return;
if(log.isDebugEnabled()) {
log.debug("Decoding query " + decodedQuery + " " +
queryStringEncoding);
}
try {
decodedQuery.duplicate( queryMB );
} catch (IOException e) {
// Can't happen, as decodedQuery can't overflow
e.printStackTrace();
}
processParameters( decodedQuery, queryStringEncoding );
}
queryStringEncoding是由什麼地方決定的呢?事實上,有幾個地方決定。第一個是CoyoteAdapter中的service方法,另外就是FormAuthenticator,這兩個地方都使用了connector.getURIEncoding()。
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
if (request == null) {
...
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
<span style="white-space:pre"> </span>}
}
也就是說跟pathInfo是一樣的,但是千萬不要以為就這樣了,其實還有另一個地方會讓整個事情變得很奇怪。在呼叫request.getParameter時,事實上會先呼叫parseParameters方法,然後才呼叫handleQueryParameters,而parseParameters就是第三個設定queryStringEncoding的地方。getCharacterEncoding首先會去找request中設定的charEncoding,找不到就去找requestHeader中contentType的編碼,還找不到就返回null,這時如果在server.xml中設定了useBodyEncodingForURI=true,則queryStringEncoding編碼就會變成預設編碼,即IS08859-1;而考慮另一種情況,如果contentType能找到這個編碼(如UTF-8),則queryStringEncoding跟隨contentType。
所以,結論是,queryStringEncoding編碼的優先順序是,第一是隨contentType,第二隨URIEncoding(即沒有設定contentType編碼,同時也沒有設定useBodyEncodingForURI),第三則是預設編碼(即沒有設定contentType,設定了useBodyEncodingForURI=true)
protected void parseParameters() {
parametersParsed = true;
Parameters parameters = coyoteRequest.getParameters();
// Set this every time in case limit has been changed via JMX
parameters.setLimit(getConnector().getMaxParameterCount());
// getCharacterEncoding() may have been overridden to search for
// hidden form field containing request encoding
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);
}
}
}
3. httpheader, 在InternalInputBuffer的parseHeader中解析,最終會呼叫到ByteChunk的toStringInternal,裡面用到的是DEFAULT_CHARSET,這個預設字符集就是ISO8859-1,意味著不能更改httpheader
public String toStringInternal() {
if (charset == null) {
charset = DEFAULT_CHARSET;
}
// new String(byte[], int, int, Charset) takes a defensive copy of the
// entire byte array. This is expensive if only a small subset of the
// bytes will be used. The code below is from Apache Harmony.
CharBuffer cb;
cb = charset.decode(ByteBuffer.wrap(buff, start, end-start));
return new String(cb.array(), cb.arrayOffset(), cb.length());
}
4. post中的引數正是上面解析queryStringEncoding中的parameters,也就是說post請求仍然是contentType中的編碼方式優先,其次就是預設的ISO8859-1。
到這裡,tomcat的編碼基本上算是分析完了。但是編碼問題涉及的點太多,比如資料庫,可以修改資料庫的編碼或者jdbc連線時指定編碼;比如一些框架,如springmvc中的ResponseBody就硬編碼了ISO8859-1,可以換用ResponseEntity,或者Response.getWriter直接輸出。總之,查到什麼地方有問題,才能對症下藥。
相關推薦
搞清tomcat中的編解碼
經常會被亂碼問題攪得頭暈腦脹。事實上,亂碼問題涉及的地方比較多,所以常常有了問題也很難定位,比如,可以發生在容器,可以發生在MVC框架,可以發生在資料庫,可以發生在響應等等。 這裡分析一下tomcat中是如何編解碼的。 以"http://localhost:8080/測試?網路=程式
Tomcat web 編解碼過程
【但是】如果server.xml中設定URIEncoding="utf-8",沒有設定useBodyEncodingForURI="true",那麼,會使用utf-8對16進位制的引數進行解碼(三個位元組對應一個字元),這時候request.getParameter()獲得亂碼的中文引數,並且通過new S
關於Tomcat上請求的編解碼問題
tomcat 編碼最近翻閱《深入分析 Java Web 技術內幕》(作者:許令波),關於Tomcat上Web請求的編解碼問題,少了一個小點,可能影響了部分讀者的理解,我特意查證了一下,特總結如下:1. 請求的PathInfo部分用Tomcat的Connector元素的URIEncoding屬性指定的編碼來解碼
實際項目中前後端傳輸字符串URL編解碼過程中遇到的一些問題
component put 傳輸 之間 body unicode編碼 方式 gpo 項目 線上版本(包括12.2,12.3版本)中,參照過濾條件在傳輸過程中經過了URL編碼及解碼過程,前後端使用的API之間的差異導致一些問題,現記錄如下: 前端URL編碼API en
Android中使用commons-codec-1.6.jar 進行Base64編解碼出現的問題
deb 分享 == 通過 HR common jar hive java 編碼時出現異常: java.lang.NoSuchMethodError: No static method encodeBase64String([B)Ljava/lang/String; i
在C語言中使用Libb64進行Base64編解碼
tar 語言 number const string ken doc get eof Libb64下載地址http://sourceforge.net/projects/libb64 以下為Demo CLibb64Demo.c #include <stdio.h&g
python中的字符串編碼問題——4.unicode編解碼(以實際工作中遇到的韓文編碼為例)
兼容 技術分享 range window下 byte 分享 pos osi eba 韓文unicode編解碼 問題是這樣,工作中遇到有韓文數據出現亂碼,說是unicode碼。 類似這樣: id name 323 52186863 149 6363
使用libpng直接在記憶體中對資料進行png編解碼
由於工作需要,需要在記憶體中直接對資料進行png編碼,然後再解碼以測試其解碼的速度,與現有的影象壓縮方法進行比較。由於初次接觸libpng,而網上這種直接在記憶體中操作的文章並不多,從頭學習要花不少的時間。鑑於此,我藉助第3方庫:opencv庫,來學習opencv是怎麼在記憶體中對資料進行操作的(open
Netty中實現MessagePack編解碼器以及解決粘包問題-參考netty權威指南2
首先maven需要增加依賴 <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId>
基於tomcat的java web專案的請求響應的編解碼問題
在一開始寫java web專案的時候,基本上每個人都不可避免地會遇到亂碼的問題,一般我們的解決方法都是這樣的:百度一下java web亂碼的常用解決方法,然後根據症狀找最符合的一個個去試,然後ok完事,然而等到下一次出現相同問題的時候,又得去翻資料。如此治標不治本,實在浪
在Qt中移植VPU編解碼程式時遇到的問題
在使用freescale開發板實現VPU的硬編碼過程中,將測試程式中關於vpu編碼函式移植到Qt中,在pro檔案中包含 "vpu_lib.h"、"vpu_io.h"的檔案路徑以及對應的連結庫檔案: INCLUDEPATH += "/mnt/hgfs/window_
JS中URL引數的編解碼
HTML中的$("form").serialize()函式,在submit按鈕點選時,將form表單中含有name的input整理成一個“name=aaa&pass=bbb”這樣的字串,使用g
[紹棠_Swift] Swift中使用Base64編解碼
/// swift Base64處理 /** * 編碼 */ func base64Encoding(plainString:String)->String
9.C++中的base64編解碼實現
#include <string> #include <iostream> #include<stdio.h> using namespace std; std::string base64_encode(unsigned char con
ubuntu下c++中base64編解碼測試和圖片編解碼測試
全棧工程師開發手冊 (作者:欒鵬) 字元陣列的base64編解碼 base64.h #include <string> std::string base64_encode(unsigned char const* , unsigned in
url中關於編解碼加號和空格的問題
今天遇到一個問題,URL中的加號傳到後臺之後變成了空格 BNn+Y6xKvmejeJmu9sS2OnRJwYhHtYXScG2ol17EUhg1oeSFE5btrT4Eh04QiwIf變成了BNn Y
Android視訊編輯器(五)音訊編解碼、從視訊中分離音訊、音訊混音、音訊音量調節等
/** * 歸一化混音 * */ public static byte[] normalizationMix(byte[][] allAudioBytes){ if (allAudioBytes == null || allAudioBytes.length
mina自定義編解碼器接收處理byte陣列(同時解決資料傳輸中的粘包、缺包問題)
我們在自定義傳輸協議時,通常都是採用位元組陣列的方式進行傳送,如何正確接收和解碼byte陣列? 假設我們自定義了傳輸協議: 位元組陣列的前4個位元組是要傳輸的資料長度,後面跟資料。我們用mina可以這樣處理 1.自定義編碼器ByteArrayEncoder.java imp
python 安裝pyautogui 和pygetwindow 報錯UnicodeDecodeError:'gbk'編解碼器無法解碼位置905中的位元組0xa2:非 gal多位元組序列 解決辦法
long_description = fh.read() UnicodeDecodeError:'gbk'編解碼器無法解碼位置9
Netty 中的訊息解析和編解碼器
本篇內容主要梳理一下 Netty 中編解碼器的邏輯和編解碼器在 Netty 整個鏈路中的位置。 前面我們在分析 ChannelPipeline 的時候說到入站和出站事件的處理都在 pipeline 中維護著,通過list的形式將處理事件的 handler 按照先後關係儲存為一個列表,有對應的事件過來就按照列