一、服务器原因(tomcat)
Tomcat 中接收请求没有设置编码的情况下,默认使用 ISO-8859-1 编码。
页面编码使用 UTF-8,get方式自然使用 UTF-8 编码;但服务器接收时没有指定解码格式,在使用 request.getParameter 获取参数值的时候,服务端会自动使用 Tomcat 默认的 ISO-8859-1 进行解码,传参带中文时自然乱码。因为编码和解码不匹配。
解决方案:
方案1:
后台可通过 String str = new String(request.getParameter(“user”).getBytes(“iso-8859-1”), “utf-8”) 进行手动转码
这一行代码里面包含3个过程:
- request.getParameter(“user”):会自动使用 Tomcat 默认的 ISO-8859-1 进行解码
- .getBytes(“iso-8859-1”):使用 ISO-8859-1 对前面的string进行编码
- new String(byte[] byte, “UTF-8”);使用 UTF-8 对字符数组byte进行解码
过程为:
UTF-8编码->iso-8859-1解码->iso-8859-1编码->UTF-8解码。
先用iso-8859-1解码,再用iso-8859-1编码就相当于回到了用UTF-8编码的起点,此时再用UTF-8解码就不会乱码了。
try {
user.setUsername(new String(request.getParameter("user").getBytes("iso-8859-1"), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
但是再实施的过程中却报错了:Invalid character found in the request target.The valid characters are defined in RFC 7230 and RFC3986.
经了解,这个问题是高版本tomcat中的新特性:就是严格按照 RFC 3986 规范进行访问解析,而 RFC 3986 规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符(! * ’ ( ) ; : @ & = + $ , / ? # [ ]),所以通过Url传中文时会报错。
解决方法是将tomcat更换为低版本的。
方案2:
修改Tomcat目录下的conf/server.xml文件:
找到如下代码:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
这段代码规定了Tomcat监听HTTP请求的端口号等信息。可以在这里添加一个属性:URIEncoding,将该属性值设置为UTF-8,即可让Tomcat(默认ISO-8859-1编码)以UTF-8的编码处理get请求。
修改完成后:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />
这样,页面使用UTF-8编码,服务器使用UTF-8解码,就不会有中文乱码了。
注意:如果是使用eclipse将项目部署到Tomcat而不是直接使用Tomcat的话,需要到eclipse的Servers项目下的server.xml文件中进行更改,而不是直接修改Tomcat中的server.xml文件,因为每一次部署时,都会使用eclipse中的Tomcat配置文件覆盖Tomcat目录下的配置文件,不修改eclipse就会导致修改失败。eclipse路径位置见下图:
二、前台页面编码,后台解码
方案3:(不依赖Tomcat的配置,推荐)
浏览器中两次URL编码,服务器中自己再做一次URL解码。
原理:
如果只进行一次encodeURI,得到的是UTF-8形式的URL,服务器端通过request.getParameter()解码查询参数(通常是ISO-8859-1)就会得到乱码。
如果进行两次encodeURI,第一次编码得到的是UTF-8形式的URL,第二次编码得到的依然是UTF-8形式的URL,但是在效果上相当于首先进行了一次UTF-8编码(此时已经全部转换为ASCII字符),再进行了一次ISO-8859-1编码,因为对英文字符来说UTF-8编码和ISO-8859-1编码的效果相同。
在服务器端,首先通过request.getParameter()自动进行第一次解码(ISO-8859-1)得到ascii字符,然后再使用UTF-8进行第二次解码,即使用java.net.URLDecoder(key,“UTF-8”)方法。
两次编码两次解码的过程为:
UTF-8编码->UTF-8(iso-8859-1)编码->iso-8859-1解码->UTF-8解码,编码和解码的过程是对称的,所以不会出现乱码。
代码实现:
jsp:
<a href="javascript:;" onclick="encode()">修改用户名</a>
注:
javascript: 是一个伪协议,表示在触发<a>默认动作时,执行一段JavaScript代码。
而 javascript:; 表示不作任何操作,这样会防止链接跳转到其他页面。
这么做是为了保留链接的样式,但不让链接执行实际操作,具体的操作交给链接的onclick事件处理。
js:
<script type="text/javascript">
function encode() {
var name = document.getElementById("username").innerHTML;
window.location.href = "${pageContext.request.contextPath}/user/getUserInfo?username=" + encodeURI(encodeURI(name));
}
</script>
后台:
try {
user.setUsername(URLDecoder.decode(user.getUsername(), "UTF-8")); // 解码
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
测试小BUG:使用eclipse的内置浏览器时发现 window.location.href 跳转失效,换用其他浏览器就好了。
参考:
- https://blog.csdn.net/u012564085/article/details/80003283
- https://blog.csdn.net/SmuEdward/article/details/51755298