过滤器Filter的案例3——解决全站的请求数据乱码问题
5、Filter高级开发(request与response)说明
(1)request & response 与装饰者模式介绍
由于开发人员在filter中可以得到代表用户请求和响应的request、response对象,因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。
Decorator设计模式的实现
1.首先看需要被增强对象继承了什么接口或父类,编写一个类也去继承这些接口或父类。
2.在类中定义一个变量,变量类型即需增强对象的类型。
3.在类中定义一个构造函数,接收需增强的对象。
4.覆盖需增强的方法,编写增强的代码。
(2)request对象的增强
Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper ,(HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。
说明:使用Decorator模式包装request对象,完全解决get、post请求方式下的乱码问题
实例: 案例五:解决全站的请求数据的乱码问题
(3)response对象的增强
Servlet API 中提供了response对象的Decorator设计模式的默认实现类HttpServletResponseWrapper ,(HttpServletResponseWrapper类实现了response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 response对象的对应方法)以避免用户在对response对象进行增强时需要实现response接口中的所有方法。
说明:使用Decorator模式包装response对象,解决数据压缩的问题
实例: 案例六:实现全站的数据的压缩
6、案例五: 解决全站的请求数据的乱码问题
(1)解决全站的请求数据的乱码:servlet实现
1.1 RequestServlet
package com.zhku.jsj144.zk.filter.requestFilter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//解决全站请求数据的乱码问题
/*
* 失败原因:
* 此时的server.xml配置是:
<Connector port="8080"protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
*/
public class RequestServlet extends HttpServlet {
//get请求
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name=request.getParameter("name");//获取参数
System.out.println("解码前---name:"+name);
//通过使用指定的charset 解码指定的 byte 数组,构造一个新的 String
//解码:字节-->字符 //编码:字符-->字节
//失败1:tomcat的默认编码:由iso-8859-1该为了utf-8
// name是通过utf-8进行编码的,而此时通过ISO-8859-1去进行解码,就会出现解码错误的情况,
// 而解码出错的情况下,有通过utf-8去进行编码,来获得传递过来的中文参数:name,就会出现乱码情况。
//name以iso8859-1的方式编码为字节数组【字符-->字节】, 然后以utf-8的方式解码为字符串【字节-->字符】
name= new String(name.getBytes("ISO-8859-1"),"UTF-8");
//失败2:不同编码方式进行编码解码,可能会失败
//name以 utf-8 的方式编码为字节数组【字符-->字节】, 然后以gbk的方式解码为字符串【字节-->字符】
name= new String(name.getBytes("UTF-8"),"gbk");
System.out.println("解码后---name:"+name);
}
//post请求
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String name=request.getParameter("name");//获取参数
System.out.println("name:"+name);
}
}
1.2 RequestServlet2
package com.zhku.jsj144.zk.filter.requestFilter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* 成功原因:
* 此时的server.xml配置是:
<Connector port="8080"protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
*/
public class RequestServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");// 获取参数
//name 以iso8859-1的方式编码为字节数组【字符-->字节】, 然后以utf-8的方式解码为字符串【字节-->字符】
// name= new String(name.getBytes("ISO-8859-1"), "UTF-8");
System.out.println("解码后---name:"+ name);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
1.3 RequestServlet3
package com.zhku.jsj144.zk.filter.requestFilter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//解决全站请求数据的乱码问题
/*
* 成功:
* 此时的server.xml配置是:
<Connectorport="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"/>
*/
public class RequestServlet3 extends HttpServlet {
//get请求
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name=request.getParameter("name");//获取参数
System.out.println("解码前---name:"+name);
//解码:字节-->字符 //编码:字符-->字节
//成功:因为tomcat的默认编码:由iso-8859-1
//name 以iso8859-1的方式编码为字节数组【字符-->字节】,然后以utf-8的方式解码为字符串【字节-->字符】
name = new String(name.getBytes("ISO-8859-1"),"UTF-8");
System.out.println("解码后---name:"+name);
}
//post请求
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String name=request.getParameter("name");//获取参数
System.out.println("name:"+name);
}
}
1.4结论
1.4.1 get方式和post方式的编码设置方式不同
自从Tomcat5.x开始,GET和POST方法提交的信息,Tomcat采用了不同的方式来处理编码,
对于POST请求,Tomcat会仍然使用request.setCharacterEncoding方法所设置的编码来处理,如果未设置,则使用默认的iso-8859-1编码。
而GET请求,则不同,Tomcat对于GET请求并不会考虑使用request.setCharacterEncoding方法设置的编码,而会永远使用iso-8859-1编码。
1.4.2 get请求方式说明
因为post方式设置比较简单,所以这里就不再详细叙述;
而get方式设置方式稍微比较复杂点,所以在此详细叙述。
get请求方式:
当我们有去修改tomcat服务器的server.xml文件,并且设置设置参数:URIEncoding="UTF-8",此时,我们直接就可以获取到传递过来的中文参数,不会产生任何乱码问题,例子:RequestServlet2
反过来说,如果没有给tomcat服务器的server.xml文件进行任何设置,则像例子:RequestServlet3一样,进行一定编码的设置,才可以获得正确的中文参数。
特别说明,如果有修改过tomcat服务器server.xml文件,在使用时,又像RequestServlet3一样,给传递过来的中文参数,进行一定的编码设置,就会导致乱码,例子:RequestServlet则会导致乱码
(2)解决全站的请求数据的乱码:过滤器实现
这里只是实现的过滤器:当我们没有去修改tomcat服务器的server.xml文件
2.1思路分析
2.2代码实现:【RequestFilter + MyHttpServletRequestWrapper + web.xml文件配置】
2.2.1RequestFilter
package com.zhku.jsj144.zk.filter.requestFilter;
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;
import javax.servlet.http.HttpServletRequest;
//解决全站的请求数据的乱码问题
import javax.servlet.http.HttpServletResponse;
//解决全站的请求数据的乱码问题的过滤器
//获取参数的三种方式
//request.getParameter("");
//request.getParameterNames();
//request.getParameterMap();
public class RequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)request;
HttpServletResponse resp=(HttpServletResponse)response;
System.out.println("开始工作。。。。。。。。。。。。。。。。");
//思路:获取参数有三种方式,我们的目的:就是对这三种方式进行改写:即增强它们的功能
//装饰者模式,我们的思路时加强request,然后把加强后的request传递下去,这样,
//在使用加强后的request的获得参数的三种方法,就可以获得设置编码后的,正确的中文参数了。
//Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper
//注意:HttpServletRequestWrapper 类实现了request 接口中的所有方法,
//但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法
//我们继承:HttpServletRequestWrapper,然后达到自己的目的
MyHttpServletRequestWrapper myRequest =new MyHttpServletRequestWrapper(req);
//增强后的request:myRequest--然后放行
chain.doFilter(myRequest, resp);//放行
}
}
2.2.2MyHttpServletRequestWrapper
package com.zhku.jsj144.zk.filter.requestFilter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
//获取参数的三种方式
//方法1:request.getParameter(""); 返回类型:String
//方法2:request.getParameterNames(); 返回类型:String【】
//方法3:request.getParameterMap(); 返回类型:Map<String,String[]>
//思路分析:重心放在方法3:request.getParameterMap();因为此方法,实际功能涵盖了方法1和方法2
//把方法3实现了,然后方法1和方法2调用方法3就可以了
//注意:通用方法getParameterMap是存在弊端的。
//因为当servlet种存在几行请求参数代码时:
//request.getParamger("aaa");request.getParamger("bbb");request.getParamger("ccc");
//每行代码都会:调用getParameterMap()方法,因此,每行代码执行时,都会获得三个参数:aaa,bbb,ccc的中文参数值,
//相当于:values[i]=new String(values[i].getBytes("iso-8859-1"),"utf-8");这行代码执行三次
//解释:代码执行第一次:中文参数,用iso-8859-1进行解码,然后用utf-8进行编码【因为一开始获得中文参数时是默认:iso-8859-1进行编码的】,所以
//此时是得到了正确的中文参数。 代码执行第二次:把正确的中文参数【utf-8编码过来的数值】,用iso-8859-1进行解码,然后用utf-8进行编码,此时
//获得了错误的中文参数,因为那个正确的中文参数,再次进行编码,解码用的是不同的码表,所以会出现乱码现象。 同理,代码执行第三次,也是跟代码执行第二次
//一样的结果,获得乱码。
//因此,我们要保证的是,对于获取过来的参数,我们只能获取一次,这样我们就可以获得正确的中文参数值了。
//解决思路:设置bool值,保证只进行一次获取,从而保证编码,解码的正确性。
//private boolean firstGet=false;//设置的bool数值
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper{
//目的:获得原有的requset
private HttpServletRequest request;
//有参构造函数,将原来的request获取,然后进行相关的加强后返回
public MyHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
this.request=request;
}
@Override
public String getParameter(String name) {
Map<String,String[]> map = getParameterMap();//调用 getParameterMap()方法
String[] values = map.get(name);
//要进一步判断是否为空
if(values!=null){
String value=values[0];//返回数组的第一个元素
return value;
}
return null;
// return super.getParameter(name);
}
@Override
public String[] getParameterValues(String name) {
Map<String,String[]> map = getParameterMap();//调用 getParameterMap()方法
String[] values = map.get(name);
return values;
// return super.getParameterValues(name);
}
//成员变量的bool数值,相当于一个全局变量
private boolean firstGet=false;//设置的bool数值
//默认是false,再调用一次后,即甚至为true。即不会第二次调用该方法
@Override
publicMap<String, String[]> getParameterMap() {
if(firstGet==false){//只有在第一次时,才会进行调用的操作
//对于get和post的请求方式做不同的处理
String method = request.getMethod();
//post的请求方式
if("post".equalsIgnoreCase(method)){
try{
request.setCharacterEncoding("utf-8");
}catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return super.getParameterMap();
}
//get的请求方式
else{
Map<String,String[]> maps = request.getParameterMap();//所有参数
Map<String,String[]> newMaps=new HashMap<String,String[]>();//保存修改后的数据
//遍历获取到的所有参数
for(int i = 0; i < maps.size(); i++)
{
Set<String> keySet = maps.keySet();
for(String key : keySet)
{
String[] values = maps.get(key);//得到数值
//对数值进行重新编码,解码
for(int j=0;j<values.length;j++)
{
try{
values[i]=newString(values[i].getBytes("iso-8859-1"),"utf-8");
}catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//因为java.util.Map所包装的HttpServletRequest对象的参数是不可改变的,
//强行更改就会报java.lang.IllegalStateException:
//Nomodifications are allowed to a locked ParameterMap异常,
//这个时候就会通过间接更改值的方式解决这个问题
//所以 此段代码作废: maps.put(key, values);//maps对于原来的可以进行覆盖
//正确思路:是重新new一个新的map,然后进行赋值,最后进行返回,即可达到要求
newMaps.put(key,values);//用新的map进行保存
}
}
firstGet=true;//设置为true后,是为了避免二次使用
// return maps;
return newMaps;//修改后的maps
}
}
//由于已经调用过了,所以直接返回上一次的数值即可,无须做任何处理
return super.getParameterMap();
}
}
2.2.3 web.xml文件配置
<!-- 解决全站的请求数据的乱码问题 -->
<filter>
<filter-name>RequestFilter</filter-name>
<filter-class>com.zhku.jsj144.zk.filter.requestFilter.RequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>