java中文乱码解决之道(六)-----javaWeb中的编码解码

在上篇博客中LZ介绍了前面两种场景(IO、内存)中的java编码解码操做,其实在这两种场景中咱们只须要在编码解码过程当中设置正确的编码解码方式通常而言是不会出现乱码的。对于咱们从事java开发的人而言,其实最容易也是产生乱码最多的地方就是web部分。首先咱们来看在javaWeb中有哪些地方存在编码转换操做。java

编码&解码

经过下图咱们能够了解在javaWeb中有哪些地方有转码:web

201501060001

用户想服务器发送一个HTTP请求,须要编码的地方有url、cookie、parameter,通过编码后服务器接受HTTP请求,解析HTTP请求,而后对url、cookie、parameter进行解码。在服务器进行业务逻辑处理过程当中可能须要读取数据库、本地文件或者网络中的其余文件等等,这些过程都须要进行编码解码。当处理完成后,服务器将数据进行编码后发送给客户端,浏览器通过解码后显示给用户。在这个整个过程当中涉及的编码解码的地方较多,其中最容易出现乱码的位置就在于服务器与客户端进行交互的过程。chrome

上面整个过程能够归纳成这样,页面编码数据传递给服务器,服务器对得到的数据进行解码操做,通过一番业务逻辑处理后将最终结果编码处理后传递给客户端,客户端解码展现给用户。因此下面我就请求对javaweb的编码&解码进行阐述。数据库

请求

客户端想服务器发送请求无非就经过四中状况:apache

一、URL方式直接访问。浏览器

二、页面连接。tomcat

三、表单get提交服务器

四、表单post提交cookie

URL方式

对于URL,若是该URL中所有都是英文的那却是没有什么问题,若是有中文就要涉及到编码了。如何编码?根据什么规则来编码?又如何来解码呢?下面LZ将一一解答!首先看URL的组成部分:网络

201501060002

在这URL中浏览器将会对path和parameter进行编码操做。为了更好地解释编码过程,使用以下URL

http://127.0.0.1:8080/perbank/我是cm?name=我是cm

将以上地址输入到浏览器URL输入框中,经过查看http 报文头信息咱们能够看到浏览器是如何进行编码的。下面是IE、Firefox、Chrome三个浏览器的编码状况:

201501080001

201501080002

201501080003

能够看到各大浏览器对“我是”的编码状况以下:

 

path部分

Query String

Firefox

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

Chrome

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

IE

E6 88 91 E6 98 AF

CE D2 CA C7

查阅上篇博客的编码可知对于path部分Firefox、chrome、IE都是采用UTF-8编码格式,对于Query String部分Firefox、chrome采用UTF-8,IE采用GBK。至于为何会加上%,这是由于URL的编码规范规定浏览器将ASCII字符非 ASCII 字符按照某种编码格式编码成 16 进制数字而后将每一个 16 进制表示的字节前加上“%”。

固然对于不一样的浏览器,相同浏览器不一样版本,不一样的操做系统等环境都会致使编码结果不一样,上表某一种状况,对于URL编码规则下任何结论都是过早的。因为各大浏览器、各个操做系统对URL的URI、QueryString编码均可能存在不一样,这样对服务器的解码势必会形成很大的困扰,下面咱们将已tomcat,看tomcat是如何对URL进行解码操做的。

解析请求的 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();     //获取URI解码集
                    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); 
    }

从上面的代码可知,对URI的解码操做是首先获取Connector的解码集,该配置在server.xml中

<Connector URIEncoding="utf-8"  />

若是没有定义则会采用默认编码ISO-8859-1来解析。

对于Query String部分,咱们知道不管咱们是经过get方式仍是POST方式提交,全部的参数都是保存在Parameters,而后咱们经过request.getParameter,解码工做就是在第一次调用getParameter方法时进行的。在getParameter方法内部它调用org.apache.catalina.connector.Request 的 parseParameters 方法,这个方法将会对传递的参数进行解码。下面代码只是parseParameters方法的一部分:

          //获取编码
             String enc = getCharacterEncoding();
            //获取ContentType 中定义的 Charset
            boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
            if (enc != null) {    //若是设置编码不为空,则设置编码为enc
                parameters.setEncoding(enc);
                if (useBodyEncodingForURI) {   //若是设置了Chartset,则设置queryString的解码为ChartSet
                    parameters.setQueryStringEncoding(enc);    
                }
            } else {     //设置默认解码方式
                parameters.setEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                }
            }

从上面代码能够看出对query String的解码格式要么采用设置的ChartSet要么采用默认的解码格式ISO-8859-1。注意这个设置的ChartSet是在 http Header中定义的ContentType,同时若是咱们须要改指定属性生效,还须要进行以下配置:

<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>

上面部分详细介绍了URL方式请求的编码解码过程。其实对于咱们而言,咱们更多的方式是经过表单的形式来提交。

表单GET

咱们知道经过URL方式提交数据是很容易产生乱码问题的,因此咱们更加倾向于经过表单形式。当用户点击submit提交表单时,浏览器会更加设定的编码来编码数据传递给服务器。经过GET方式提交的数据都是拼接在URL后面(能够当作query String??)来提交的,因此tomcat服务器在进行解码过程当中URIEncoding就起到做用了。tomcat服务器会根据设置的URIEncoding来进行解码,若是没有设置则会使用默认的ISO-8859-1来解码。假如咱们在页面将编码设置为UTF-8,而URIEncoding设置的不是或者没有设置,那么服务器进行解码时就会产生乱码。这个时候咱们通常能够经过new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8") 的形式来获取正确数据。

表单POST

对于POST方式,它采用的编码也是由页面来决定的即contentType。当我经过点击页面的submit按钮来提交表单时,浏览器首先会根据ontentType的charset编码格式来对POST表单的参数进行编码而后提交给服务器,在服务器端一样也是用contentType中设置的字符集来进行解码(这里与get方式就不一样了),这就是经过POST表单提交的参数通常而言都不会出现乱码问题。固然这个字符集编码咱们是能够本身设定的:request.setCharacterEncoding(charset) 。


-----原文出自:http://cmsblogs.com/?p=1510,请尊重做者辛勤劳动成果,转载说明出处.

-----我的站点:http://cmsblogs.com

相关文章
相关标签/搜索