巴西龟不吃东西,小屯村,陕西政府
编码&解码
通过下图我们可以了解在javaweb中有哪些地方有转码:
用户想服务器发送一个http请求,需要编码的地方有url、cookie、parameter,经过编码后服务器接受http请求,解析http请求,然后对url、cookie、parameter进行解码。在服务器进行业务逻辑处理过程中可能需要读取数据库、本地文件或者网络中的其他文件等等,这些过程都需要进行编码解码。当处理完成后,服务器将数据进行编码后发送给客户端,浏览器经过解码后显示给用户。在这个整个过程中涉及的编码解码的地方较多,其中最容易出现乱码的位置就在于服务器与客户端进行交互的过程。
上面整个过程可以概括成这样,页面编码数据传递给服务器,服务器对获得的数据进行解码操作,经过一番业务逻辑处理后将最终结果编码处理后传递给客户端,客户端解码展示给用户。所以下面我就请求对javaweb的编码&解码进行阐述。
请求
客户端想服务器发送请求无非就通过四中情况:
1、url方式直接访问。
2、页面链接。
3、表单get提交
4、表单post提交
url方式
对于url,如果该url中全部都是英文的那倒是没有什么问题,如果有中文就要涉及到编码了。如何编码?根据什么规则来编码?又如何来解码呢?下面将一一解答!首先看url的组成部分:
在这url中浏览器将会对path和parameter进行编码操作。为了更好地解释编码过程,使用如下url
http://127.0.0.1:8080/perbank/我是cm?name=我是cm
将以上地址输入到浏览器url输入框中,通过查看http 报文头信息我们可以看到浏览器是如何进行编码的。下面是ie、firefox、chrome三个浏览器的编码情况:
可以看到各大浏览器对“我是”的编码情况如下:
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 |
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) 。
解决url中文乱码问题
我们主要通过两种形式提交向服务器发送请求:url、表单。而表单形式一般都不会出现乱码问题,乱码问题主要是在url上面。通过前面几篇博客的介绍我们知道url向服务器发送请求编码过程实在是实在太混乱了。不同的操作系统、不同的浏览器、不同的网页字符集,将导致完全不同的编码结果。如果程序员要把每一种结果都考虑进去,是不是太恐怖了?有没有办法,能够保证客户端只用一种编码方法向服务器发出请求?
有!这里我主要提供以下几种方法
javascript
使用javascript编码不给浏览器插手的机会,编码之后再向服务器发送请求,然后在服务器中解码。在掌握该方法的时候,我们需要料及javascript编码的三个方法:escape()、encodeuri()、encodeuricomponent()。
escape
采用sio latin字符集对指定的字符串进行编码。所有非ascii字符都会被编码为%xx格式的字符串,其中xx表示该字符在字符集中所对应的16进制数字。例如,格式对应的编码为%20。它对应的解码方法为unescape()。
事实上escape()不能直接用于url编码,它的真正作用是返回一个字符的unicode编码值。比如上面“我是cm”的结果为%u6211%u662fcm,其中“我”对应的编码为6211,“是”的编码为662f,“cm”编码为cm。
注意,escape()不对"+"编码。但是我们知道,网页在提交表单的时候,如果有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处理成空格。所以,使用的时候要小心。
encodeuri
对整个url进行编码,它采用的是utf-8格式输出编码后的字符串。不过encodeuri除了ascii编码外对于一些特殊的字符也不会进行编码如:! @ # $& * ( ) = : / ; ? + '。
encodeuricomponent
把uri字符串采用utf-8编码格式转化成escape格式的字符串。相对于encodeuri,encodeuricomponent会更加强大,它会对那些在encodeuri()中不被编码的符号(; / ? : @ & = + $ , #)统统会被编码。但是encodeuricomponent只会对url的组成部分进行个别编码,而不用于对整个url进行编码。对应解码函数方法decodeuricomponent。
当然我们一般都是使用encodeuri方来进行编码操作。所谓的javascript两次编码后台两次解码就是使用该方法。javascript解决该问题有一次转码、两次转码两种解决方法。
一次转码
javascript转码:
var url = '<s:property value="webpath" />/showmoblieqrcode.servlet?name=我是cm'; window.location.href = encodeuri(url);
转码后的url:http://127.0.0.1:8080/perbank/showmoblieqrcode.servlet?name=%e6%88%91%e6%98%afcm
后台处理:
string name = request.getparameter("name"); system.out.println("前台传入参数:" + name); name = new string(name.getbytes("iso-8859-1"),"utf-8"); system.out.println("经过解码后参数:" + name);
输出结果:
前台传入参数:??????cm
经过解码后参数:我是cm
二次转码
javascript
var url = '<s:property value="webpath" />/showmoblieqrcode.servlet?name=我是cm'; window.location.href = encodeuri(encodeuri(url));
转码后的url:http://127.0.0.1:8080/perbank/showmoblieqrcode.servlet?name=%25e6%2588%2591%25e6%2598%25afcm
后台处理:
string name = request.getparameter("name"); system.out.println("前台传入参数:" + name); name = urldecoder.decode(name,"utf-8"); system.out.println("经过解码后参数:" + name);
输出结果:
前台传入参数:e68891e698afcm
经过解码后参数:我是cm
filter
使用过滤器,过滤器提供两种,第一种设置编码,第二种直接在过滤器中进行解码操作。
过滤器1
该过滤器是直接设置request的编码格式的。
public class characterencoding implements filter { private filterconfig config ; string encoding = null; public void destroy() { config = null; } public void dofilter(servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception { request.setcharacterencoding(encoding); chain.dofilter(request, response); } public void init(filterconfig config) throws servletexception { this.config = config; //获取配置参数 string str = config.getinitparameter("encoding"); if(str!=null){ encoding = str; } } }
配置:
<!-- 中文过滤器的配置 --> <filter> <filter-name>chineseencoding</filter-name> <filter-class>com.test.filter.characterencoding</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>chineseencoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器2
该过滤器在处理方法中将参数直接进行解码操作,然后将解码后的参数重新设置到request的attribute中。
public class characterencoding implements filter { protected filterconfig filterconfig ; string encoding = null; public void destroy() { this.filterconfig = null; } /** * 初始化 */ public void init(filterconfig filterconfig) { this.filterconfig = filterconfig; } /** * 将 instr 转为 utf-8 的编码形式 * * @param instr 输入字符串 * @return utf - 8 的编码形式的字符串 * @throws unsupportedencodingexception */ private string toutf(string instr) throws unsupportedencodingexception { string outstr = ""; if (instr != null) { outstr = new string(instr.getbytes("iso-8859-1"), "utf-8"); } return outstr; } /** * 中文乱码过滤处理 */ public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain chain) throws ioexception, servletexception { httpservletrequest request = (httpservletrequest) servletrequest; httpservletresponse response = (httpservletresponse) servletresponse; // 获得请求的方式 (1.post or 2.get), 根据不同请求方式进行不同处理 string method = request.getmethod(); // 1. 以 post 方式提交的请求 , 直接设置编码为 utf-8 if (method.equalsignorecase("post")) { try { request.setcharacterencoding("utf-8"); } catch (unsupportedencodingexception e) { e.printstacktrace(); } } // 2. 以 get 方式提交的请求 else { // 取出客户提交的参数集 enumeration<string> paramnames = request.getparameternames(); // 遍历参数集取出每个参数的名称及值 while (paramnames.hasmoreelements()) { string name = paramnames.nextelement(); // 取出参数名称 string values[] = request.getparametervalues(name); // 根据参数名称取出其值 // 如果参数值集不为空 if (values != null) { // 遍历参数值集 for (int i = 0; i < values.length; i++) { try { // 回圈依次将每个值调用 toutf(values[i]) 方法转换参数值的字元编码 string vlustr = toutf(values[i]); values[i] = vlustr; } catch (unsupportedencodingexception e) { e.printstacktrace(); } } // 将该值以属性的形式藏在 request request.setattribute(name, values); } } } // 设置响应方式和支持中文的字元集 response.setcontenttype("text/html;charset=utf-8"); // 继续执行下一个 filter, 无一下个 filter 则执行请求 chain.dofilter(request, response); } }
配置:
<!-- 中文过滤器的配置 --> <filter> <filter-name>chineseencoding</filter-name> <filter-class>com.test.filter.characterencoding</filter-class> </filter> <filter-mapping> <filter-name>chineseencoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
其他
1、设置pageencoding、contenttype
<%@ page language="java" contenttype="text/html;charset=utf-8" pageencoding="utf-8"%>
2、设置tomcat的uriencoding
在默认情况下,tomcat服务器使用的是iso-8859-1编码格式来编码的,uriencoding参数对get请求的url进行编码,所以我们只需要在tomcat的server.xml文件的<connector>标签中加上uriencoding="utf-8"即可。
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
浅析我对 String、StringBuilder、StringBuffer 的理解
使用IDEA搭建SSM框架的详细教程(spring + springMVC +MyBatis)
Springboot整合freemarker 404问题解决方案
引入mybatis-plus报 Invalid bound statement错误问题的解决方法
网友评论