Spring MVC 使用介绍(十)—— 编码
一、概述
客户端与服务器端在交互过程中,需要将字符以某种编码方式转化为字节流进行传输,因此涉及字符的编码和解码。某一方以编码方案A编码,另一方须以同样的编码方案解码,否则会出现乱码。
客户端与服务器端的交互可分为三种:
- 服务端向客户端传递数据
- 客户端向服务端传递数据(POST)
- 客户端向服务端传递数据(GET)
服务端在不指定编码方式时,默认使用 ISO-8859-1 解码
客户端在使用 encodeURIComponent() 方法时,使用 UTF-8 编码
二、服务端向客户端传递数据
服务端向客户端传递数据依赖HttpServletResponse类提供的方法,需要两步:
1、以某种编码写数据
response.getOutputStream().write("异常处理hello".getBytes("UTF-8"));
或者
response.setCharacterEncoding("UTF-8");
response.getWriter().write("异常处理hello");
2、添加响应头,告知客户端解码方式
response.addHeader("content-type", "text/html;charset=UTF-8");
三、客户端向服务端传递数据(POST)
客户端向服务端传递数据依赖HttpServletRequest类提供的方法,需要两步:
1、设置解码方式
request.setCharacterEncoding("UTF-8");
2、读取参数
String name = request.getParameter("name");
Integer age = Integer.valueOf(request.getParameter("age"));
测试请求包(使用Fiddler发起)
POST http://localhost:8080/java-web-test/encoding/1 HTTP/1.1 Content-Type: application/x-www-form-urlencoded name=%E9%80%89%E6%8B%A9%E5%A4%A7%E4%BA%8E%E5%8A%AA%E5%8A%9B&age=859
四、客户端向服务端传递数据(GET)
客户端通过GET方式(即通过url)传递参数,须以如下方式解析:
String name = new String(request.getParameter("name").getBytes("ISO-8859-1"), "UTF-8");
测试:http://localhost:8080/java-web-test/encoding/1?name=%E9%80%89%E6%8B%A9&age=52
即服务端默认以 ISO-8859-1 编码方式解析,解析时须以 ISO-8859-1 编码方式还原为字节码,再在以 UTF-8 编码方式解码
补充:
浏览器的encodeURIComponent()编码方式是将特定字符以 UTF-8 方式编码为二进制,再以%为分隔、以十六进制方式展示,如:
encodeURIComponent('选择'); // 输出:%E9%80%89%E6%8B%A9
java等价代码:
String str = URLEncoder.encode("选择", "UTF-8");
同样,decodeURIComponent() 解码方式是将以%为分隔的十六进制字符转换为二进制,再以 UTF-8 方式解码
decodeURIComponent("%E9%80%89%E6%8B%A9") // 输出:选择
java等价代码:
String str = URLDecoder.decode("%E9%80%89%E6%8B%A9", "UTF-8");
五、web.xml中设置编码方式
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> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器的部分源码如下:
@Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) { request.setCharacterEncoding(this.encoding); if (this.forceEncoding) { response.setCharacterEncoding(this.encoding); } } filterChain.doFilter(request, response); }
即设置了请求与响应内容区的编码方式,但对GET请求无处理,因此仍然需要另外处理,如下:
@RestController public class TestController { @RequestMapping(value = "/hello") public Map<String, Object> helloWorld(@RequestParam("name") String name) throws UnsupportedEncodingException { Map<String, Object> map = new HashMap<String, Object>(); map.put("name", new String(name.getBytes("ISO-8859-1"), "UTF-8")); return map; } }
测试:http://localhost:8080/myweb/hello?name=%E7%A8%8B%E5%90%9B&age=29
例外一种解决方法为添加过滤器,详细可参考javaweb学习总结(四十三)——Filter高级开发
参考:
javaweb学习总结(七)——HttpServletResponse对象(一)
javaweb学习总结(十)——HttpServletRequest对象(一)