中文乱码问题案例分析
?
项目采用的是SSH框架技术,模板视图用的是FreeMarker,对于编码问题做了以下的配
① tomcat 服务器没配置URIEncoding参数。
② struts2配置文件配置了如下的参数:
?<!-- 编码 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>
③ web.xml进行了如下配置:
<!-- 编码处理过滤器 -->
??? <filter>
?????? <filter-name>encodingFilter</filter-name>
?????? <filter-class>
?????????? org.springframework.web.filter.CharacterEncodingFilter
?????? </filter-class>
?????? <init-param>
?????????? <param-name>encoding</param-name>
?????????? <param-value>utf-8</param-value>
?????? </init-param>
?????? <init-param>
?????????? <param-name>forceEncoding</param-name>
?????????? <param-value>true</param-value>
?????? </init-param>
</filter>
现在来看一下这三者分别的用途,
解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.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){...}
?????? ??? if (conv != null) {
?????? ?????? try {
?????? ??????????? conv.convert(bc, cc, cc.getBuffer().length -? cc.getEnd());
?????? ??????????? uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());
?????? ??????????? return;
?????? ??????????? } catch (IOException e) {...}
?????? ??? }
?????? }
??????? // 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);
??? }
也就是说步骤是大致是这样的(因为没仔细研究tomcat源码,所以顺序可能不会很正确):
从上面的 URL 编码和解码过程来看,比较复杂,而且编码和解码并不是我们在应用程序中能完全控制的,所以在我们的应用程序中应该尽量避免在 URL 中使用非 ASCII 字符,不然很可能会碰到乱码问题,当然在我们的服务器端最好设置 <Connector/> 中的 URIEncoding 和 useBodyEncodingForURI 两个参数。
不过要说一点,虽然这里如果进行了指定,但是由于不同浏览器对URL进行编码的方式不同,也会出现乱码,以下是分析:
?由于我什么都没有设置,所以可以猜到,如果有中文路径或者是中文参数的URL,一定会出现中文乱码。假设我进行了设置,但是如果我在页面中没对中文路径和中文参数URL进行编码处理,那么由于不同浏览器的编码方式不同也会造成中文乱码问题,也就说终极解决方案是:所以最好不要在URL中直接使用非ASCII字符,而采用URL Encode编码过的字符串%.
POST 表单的编解码
在前面提到了 POST 表单提交的参数的解码是在第一次调用 request.getParameter 发生的,POST 表单参数传递方式与 QueryString 不同,它是通过 HTTP 的 BODY 传递到服务端的。当我们在页面上点击 submit 按钮时浏览器首先将根据 ContentType 的 Charset 编码格式对表单填的参数进行编码然后提交到服务器端,在服务器端同样也是用 ContentType 中字符集进行解码。所以通过 POST 表单提交的参数一般不会出现问题,而且这个字符集编码是我们自己设置的,可以通过 request.setCharacterEncoding(charset) 来设置。
另外针对 multipart/form-data 类型的参数,也就是上传的文件编码同样也是使用 ContentType(这里所说的ContentType是指http头的ContentType,而不是在网页中meta中的ContentType) 定义的字符集编码,值得注意的地方是上传文件是用字节流的方式传输到服务器的本地临时目录,这个过程并没有涉及到字符编码,而真正编码是在将文件内容添加 到 parameters 中,如果用这个编码不能编码时将会用默认编码 ISO-8859-1 来编码。
通过上面的分析我们可以知道,post提交的编码解码跟服务器端的设置没有任何关系,而且也别妄想通过使用request.setCharacterEncoding()方法来解决url传递参数中文乱码的问题。
? if(encoding != null && (forceEncoding || request.getCharacterEncoding() == null))
??????? {
??????????? request.setCharacterEncoding(encoding);
??????????? if(forceEncoding && responseSetCharacterEncodingAvailable)
??????????????? response.setCharacterEncoding(encoding);
??????? }
request.setCharacterEncoding(encoding);
现在就知道为什么我配置了中文编码过滤器还是出错的原因了嘛,这个过滤器只会对post提交有效,这是防止你在页面中没有设置ContentType的charset而导致中文乱码。
?
?? 经过上面的分析总结一下终极解决办法:
1、? 在Tomcat中设置URIEncoding和 useBodyEncodingForURI 这两个参数。
2、? 页面中或js中有中文路径或从参数,先进行编码,编码字符集和上面tomcat中配置的一样。
?