首先,来说一下HTML和JSP的区别:
HTML属于前台,纯展示页面,请求HTML时,容器(如Tomcat)会读取HTML文件的内容,通过socket遵循HTTP协议发送到浏览器;
JSP属于后台,请求JSP时,容器会首先读取JSP文件的内容,然后根据JSP文件内容生成对应的java类文件(这个类是继承自HttpJspBase的,而HttpJspBase是继承自HttpServlet的,所以JSP生成了一个对应的Servlet),接着编译出class文件(在Tomcat中,生成java和class文件是由 org.apache.jasper.compiler.Compiler 完成的),然后由Tomcat类加载器加载class文件,由生成的Servlet来处理请求,最后由HttpServletResponse以"text/html;charset=UTF-8"的形式发送到浏览器,显示HTML。
通常,在JSP中的Java代码中,或在Servlet的doGet、doPost中,调用request.getParameter(name)获取前台传来的参数时,经常会出现乱码问题。
乱码问题,究其原因,就是因为 前台页面编码、前台发请求时传参数所用编码、后台JSP/Servlet中从socket流读取参数解析参数时所用编码不统一。
首先,编写HTML文件时,就应该明确规定页面采用的编码:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
页面采用的编码,就是form提交时采用的编码,如果页面不明确指定编码,就会由浏览器自己决定了,这就出现了极大的不确定性。
指定编码时:
不指定编码时:
对于Chrome、Firefox也是如此。
Tomcat读取JSP页面内容采用的编码:
这张图画的有问题,Tomcat的代码写的太绕了,想知道详情,就去看代码吧[ParserController.determineSyntaxAndEncoding()]。
因此,如果在JSP中pageEncoding和contentType都不指定的话,显示出来的页面可能会出乱码。
当使用JSP/Servlet获取前台传递的参数时:
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%> <%@page import="java.util.Enumeration"%> <% out.println("Headers<br>"); Enumeration headers = request.getHeaderNames(); String name; while(headers.hasMoreElements()) { name = headers.nextElement()+""; out.println(name + " : " + request.getHeader(name)); out.println("<br>"); } out.println("<br>"); String v = request.getParameter("v"); String username = request.getParameter("username"); String password = request.getParameter("password"); out.println("Default encoding:"); out.println("v=" + v); out.println("username=" + username); out.println("password=" + password); out.println("<br>"); out.println("GB2312:"); out.println("v=" + new String(v.getBytes("ISO-8859-1"), "GB2312")); out.println("username=" + new String(username.getBytes("ISO-8859-1"), "GB2312")); out.println("password=" + new String(password.getBytes("ISO-8859-1"), "GB2312")); out.println("<br>"); out.println("UTF-8:"); out.println("v=" + new String(v.getBytes("ISO-8859-1"), "UTF-8")); out.println("username=" + new String(username.getBytes("ISO-8859-1"), "UTF-8")); out.println("password=" + new String(password.getBytes("ISO-8859-1"), "UTF-8")); out.println("<br>"); %>
如果HTML中没有在meta的http-equiv中指定Content-Type,提交form的时候:
如果HTML中指定了Content-Type:
Tomcat在读取参数时,如果在HTTP Request Header中没有指定Content-Type,读取参数流使用的编码是“ISO-8859-1”,这个参数由request.setCharacterEncoding()指定,不过需要在第一次执行request.getParameter()之前调用才可以。通过这个API,可以使用编码过滤器的方式来免去JSP/Servlet中获取参数时需要进行的编码转换。
package jspservlet; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class EncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } @Override public void destroy() { } }
添加了这个EncodingFilter之后,再调用request.getParameter()就不用再new String(value.getBytes("ISO-8859-1"), "UTF-8")了。