浏览器在向服务器发送请求时会对携带的请求参数进行编码(UTF-8格式),服务器在接收到请求参数时会对其进行解码,因为浏览器与服务器编码格式不一样产生乱码。不一样服务器默认编码格式不一样,Tomcat默认ISO-8859-1。
html
get请求有三种解决方案。
java
a. 经过先编码再解码方式。服务器以不一样编码格式解码后致使乱码,此时能够经过先以与服务器相同的编码格式将字符串编码成原始的字节流,再经过String类的构造方法解码生成正确的字符串。代码以下:web
String newJoo = new String(joo.getBytes("ISO-8859-1"), "UTF-8"); //joo为字符串
b. 修改tomcat服务器配置文件,设置url编码格式。tomcat安装目录下conf文件夹内server.xml配置文件,在<Connector connectionTimeout="20000" port="8090" protocol="HTTP/1.1" redirectPort="8443" />中添加 URIEncoding="UTF-8"属性。该方法对post乱码无做用,由于它只能设置url中携带的请求参数,而post请求参数存放在请求体中。浏览器
c. 在页面中用js函数如:encoding(),进行编码,后台用java代码解码。这种方法麻烦,就很少说了。tomcat
三种方法各有各的缺陷,使用a方法须要在每一个servlet类中都添加那段代码,形成代码冗余,可维护性差;使用b方法能够达到一劳永逸的效果,可是若是服务器中有其余项目须要用不一样的url编码格式怎么办?因此这样不灵活,不友好,并且对post请求乱码无效;c方法的缺陷同a方法。服务器
post请求乱码解决方式很简单,只须要一行代码。request对象中有个设置编码格式的方法:app
request.setCharacterEncoding("utf-8")
一样的,这种方法也有缺陷。首先,它形成代码冗余,使可维护性变差,其次,它只对请求体中的参数有做用,对get请求方式乱码无效。
ide
在Servlet中获取请求参数有三种方法:getParameter(),getParameterValues(),getParameterMap(),getParameter()返回String类型,用于获取单个请求参数,getParameterValues()返回String[]类型,也用于获取单个请求参数,只不过这个请求参数包含多个值,如复选框,getParameterMap()返回值为Map<String, String[]>,用于获取全部请求参数。函数
ServletRequest接口中定义了这三个方法,tomcat实现这三个方法,但实现源码并无作编码相关的处理,若是咱们本身定义一个类实现ServletRequest接口而后重写这三个方法,并在其中解决post和get乱码不就既能够一劳永逸解决问题,避免代码冗余,同时还保证了灵活性,确保不会影响同服务器中的其它项目。可是实现ServletRequest接口就必须实现其中的全部方法,工做量太大!post
sun公司早已考虑到这个问题,很体贴的设计了一个包装类实现ServletRequest接口,咱们只须要定义一个类继承HttpServletRequestWrapper类,并重写三个方法就能达到目的。先来看看HttpServletRequestWrapper类的继承关系:
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest 能够看到它继承了ServletRequestWrapper类,实现了HttpServletRequest接口,因此HttpServletRequestWrapper实现了ServletRequest接口的全部方法。
咱们的目的是要让全部Servlet都使用重写的方法,上面咱们解决了怎么重写三个获取请求参数的方法,可是还有一个问题——如何才能让全部Servlet都使用咱们重写的方法,即在哪里重写这三个方法。解决这个问题首先要明白Servlet的生命周期,这里简单的介绍下,tomcat启动时实例化Servlet对象并执行其中的init()方法进行初始化操做,当tomcat接收到请求时会建立HttpServletRequest和HttpServletResponse对象,而后执行Servlet对象中的Service()方法判断请求方式并调用doGet()或者doPost()方法,这里会将两对象看成参数传递给doGet()和doPost()方法,当tomcat正常stop时执行Servlet类中的destory()方法进行销毁操做。知道了Servlet的生命周期咱们知道,若是要让全部Servlet类都使用重写的方法咱们能够修改tomcat的源代码,使它在建立HttpServletRequest和HttpServletResponse对象的过程当中建立的是咱们自定义对象,但这样工做量很大,并且还会影响其余项目,不灵活也不友好。到这里java web三大组件之一Filter就浮出水面了。
Filter——过滤器,能够先于全部Servlet执行,咱们在自定义的Filter中定义个类继承HttpServletRequestWrapper,重写三个获取请求参数方法,再将它传递给Servlet就能够达到咱们的目的。
web.xml配置
<filter> <filter-name>encodingFilter</filter-name> <filter-class>ff.charset.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
自定义Filter类
public class EncodingFilter implements Filter{ private String encoding; // 编码格式 @Override public void init(FilterConfig filterConfig) throws ServletException { // 从web.xml中获取编码格式 this.encoding = filterConfig.getInitParameter("encoding"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 向下转型成HttpServletRequest HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; // 设置数据响应编码格式 response.setContentType("text/html;charset=" + encoding); // 建立装饰类 MyHttpRequest myhr = new MyHttpRequest(req, encoding); // 放行 chain.doFilter(myhr, res); } @Override public void destroy() { // TODO Auto-generated method stub } }
自定义类继承HttpServletRequestWrapper
上面提到了getParameterMap()用于获取全部请求参数,那它也能够获取单个请求参数,全部咱们只须要重写这个方法,而后其余两方法调用此方法就能够。
public class MyHttpRequest extends HttpServletRequestWrapper { private HttpServletRequest req; // 原始请求对象 private String encoding; // 编码格式 private boolean flag = true; // 开关 public MyHttpRequest(HttpServletRequest request, String encoding) { super(request); this.req = request; this.encoding = encoding; } @Override public String getParameter(String name) { Map<String, String[]> map = getParameterMap(); String[] values = map.get(name); if (null == values) { return null; } return values[0]; } @Override public String[] getParameterValues(String name) { Map<String, String[]> map = getParameterMap(); return map.get(name); } @Override public Map<String, String[]> getParameterMap() { String method = req.getMethod(); if (method.equalsIgnoreCase("post")) { try { req.setCharacterEncoding(encoding); return req.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get") && true == flag) { // 经过flag使这段代码一次请求只执行一次,由于执行屡次至关于解码再编码屡次,形成乱码 Map<String, String[]> map = req.getParameterMap(); // 对象赋值传递的是地址,map指向req地址,修改map值至关于修改req中属性值 Set<String> set = map.keySet(); // 迭代遍历map中全部值,经过解码再编码解决get乱码 Iterator<String> iterator = set.iterator(); while (iterator.hasNext()) { String[] values = map.get(iterator.next()); if (null != values) { for (int i = 0; i < values.length; i++) { try { values[i] = new String(values[i].getBytes("ISO-8859-1"), encoding); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } flag = false; return map; } // 其它请求执行原始获取参数方法,除了get post外还有其余请求方式 return super.getParameterMap(); } }