前提概要:编码和解码采用的字符集不同就会出现乱码。那么浏览器到服务器,服务器到浏览器之间通信过程中有过哪些编解码过程?默认编解码的字符集是什么?如何设置?下文将从这些方面进行详细说明!
常用字符集:
- GB2313:中国制定的字符集,每个中文占两个字节。
- GBK:是GB2312的扩展,向下兼容GB2312。
- ISO8859-1:单字节,不支持中文。
- UTF-8:支持中文,每个中文三个字节。
默认字符集
操作系统"Windows"中文版的默认编码GB2312(cmd命令输入chcp可以查看,936代表GB2312)
一、浏览器 - - -》服务器
1. 浏览器的编码
1.1 url中编码
1. 为什么会出现url编码?
一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。比如,世界上有英文字母的网址"http://www.abc.com",但是没有希腊字母的网址"http://www.aβγ.com"。这是因为网络标准RFC 1738做了硬性规定:只有字母和数字[0-9a-zA-Z]、一些特殊符号"$-_.+!*'(),"[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL
。
这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致"URL编码"成为了一个混乱的领域。
2. 什么是url编码?
将中文等按某一字符集来编码,然后将其十六进制每两个用一个%隔开,如:中国 —》%E4%B8%AD%E5%9B%BD;当然像谷歌和火狐浏览器地址栏中直接显示的中文,抓包可以看到编码后的。
3. url组成
url组成:域名:端口/contextPath/servletPath/pathInfo?queryString
。下面主要将url从两部分说明:1. 资源路径(pathInfo);2. 查询参数(queryString)
1.2 个人结论
下文对地址栏中url的资源地址和请求参数以get和post两种请求方式,网页中的请求和直接在地址栏输入时不同浏览器编码方式:不同浏览器中URL的编码方式
- 火狐和谷歌浏览器的默认编码为
utf-8
; - 如果直接在地址栏中输入时,pathInfo(路径信息)和queryString(请求参数)中的都是采用浏览器的默认编码方式;
- 如果是在html页面中的请求,如
超链接:<a href="/中国/a?str=中国">
、表单提交。pathInfo(前一个中国)仍然采用浏览器的默认编码,而queryString(后一个中国)/表单数据(作为post请求的请求体或者get请求url后面的参数)则采用页面设置的编码。
//html页面
<form action="中国.do?contry=中国" method="post" enctype="multipart/form-data">
<table>
<tr>
<td>姓名</td>
<td><input name="name" type="text"></td>
<td>年龄</td>
<td><input name="age" type="text"></td>
</tr>
<tr><input type="submit" value="提交"></tr>
</table>
</form>
2. 服务器端如何解码?
服务器端默认解码和编码方式是
ISO-8859-1
。当然要服务器端设置相同的解码字符集才不会乱码,具体设置如下:
2.1 服务器软件中设置(tomcat为例)
修改服务器端对url参数的默认编码:
在tomcat的server.xml
中,设置<Connector ….>
元素的属性URIEncoding="UTF-8"
即可。(默认没有设置此属性)。
例如:<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8"/>
注意:
1、设置<Connector ….>
元素的属性useBodyEncodingForURI="true"
,意思是请求体和url参数使用相同的编码格式。通过设置这两个属性,既可以解决get方式的乱码,又可以解决post方式的乱码。
2、通过修改server.xml
指定服务器对get和post统一按照utf-8
解码,要求tomcat管理下的所有web应用都要使用utf-8编码,即所有的jsp、html页面都使用utf-8编码。
2.2 通过request.setCharacterEncoding("utf-8”)即可解决乱
request.setCharacterEncoding("utf-8”)
只对请求体里面的参数有效;如果参数跟在请求行中的url后边,它就无能为力了。因此这个可以适用post请求
。但是不适用于url中(资源地址或参数中)含中文的get请求和url也带参数的post请求。
2.3 借助getBytes(),反编码
既然服务器会进行
iso-8859-1
解码,那么我就让它这样解码,当其将这种解码的结果封装在request对象中时我可以通过getBytes()
来获取其原始字节码,然后再编码,具体如下:
String name = request.getParameter("name”);//得到乱码
//name.getBytes("iso-8859-1"):采用iso-8859-1还原到字节码;然后再对字节码用utf-8来编码得到正确的字符串。
name = new String(name.getBytes("iso-8859-1"),"utf-8”);
注意: name.getBytes();如果不指定编码,默认按照gb2312进行编码。
2.4 在springmvc中采用CharacterEncodingFilter拦截器
当post请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤。
我想搞清楚的是这个是在服务器按iso-8859-1
解码后,然后这个拦截器要求其按iso-8859-1
还原字节码然后再用’utf-8’的吗?
他对请求体参数和url中的参数都有效吗?
web.xml中的配置:
<filter>
<filter-name>CharacterEncodingFilter</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>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
结合CharacterEncodingFilter源码解读
这里的forceEncoding是决定其两个成员变量private boolean forceRequestEncoding = false; private boolean forceResponseEncoding = false;
的值,默认为false,如果为true则对请求或响应设置如下:request.setCharacterEncoding(encoding);
或response.setCharacterEncoding(encoding);
我想到目前,已经解决了我前面的问题!根据不同配置可以对响应和请求做这两个操作,但这样设置的局限性前面也提到过。request.setCharacterEncoding(encoding);
只对请求体有用(不过好像get方式的编码问题jdk已经解决了);response.setCharacterEncoding(encoding);
只规定了服务器端的编码字符集而没有告诉浏览器。
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
public class CharacterEncodingFilter extends OncePerRequestFilter {
@Nullable
private String encoding;
private boolean forceRequestEncoding = false;
private boolean forceResponseEncoding = false;
public CharacterEncodingFilter() {
}
public CharacterEncodingFilter(String encoding) {
this(encoding, false);
}
public CharacterEncodingFilter(String encoding, boolean forceEncoding) {
this(encoding, forceEncoding, forceEncoding);
}
public CharacterEncodingFilter(String encoding, boolean forceRequestEncoding, boolean forceResponseEncoding) {
Assert.hasLength(encoding, "Encoding must not be empty");
this.encoding = encoding;
this.forceRequestEncoding = forceRequestEncoding;
this.forceResponseEncoding = forceResponseEncoding;
}
public void setEncoding(@Nullable String encoding) {
this.encoding = encoding;
}
@Nullable
public String getEncoding() {
return this.encoding;
}
/*
****************************************************
~~~~~~~~上面配置的forceEncoding就是设置的这个属性这个就是属性~~~~~~
****************************************************
*/
public void setForceEncoding(boolean forceEncoding) {
this.forceRequestEncoding = forceEncoding;
this.forceResponseEncoding = forceEncoding;
}
public void setForceRequestEncoding(boolean forceRequestEncoding) {
this.forceRequestEncoding = forceRequestEncoding;
}
public boolean isForceRequestEncoding() {
return this.forceRequestEncoding;
}
public void setForceResponseEncoding(boolean forceResponseEncoding) {
this.forceResponseEncoding = forceResponseEncoding;
}
public boolean isForceResponseEncoding() {
return this.forceResponseEncoding;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String encoding = getEncoding();
if (encoding != null) {
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
}
二、服务器 - - -》浏览器
1. 服务器编码
前面也说了,服务器端默认解码和编码方式是
ISO-8859-1
,当然我们是可以设置的!
1.1 设置响应的编码
使用:response.setContentType("text/html;charset=utf-8");
;其是response.setCharacterEncoding("utf-8");
(设置响应流的编码) 和 response.setHeader("content-type","text/html;charset=utf-8");
(设置响应数据的格式,告诉浏览器我的响应体MIME类型和我采用的编码)的结合。
参考:
浏览器到服务器间的编解码问题1