过滤器原理和例子

编写处理request,response字符集的过滤器:

1.HttpServletRequest

GET方式提交的请求无法通过设置request字符集解决编码问题,可以理解为是getParameter的问题,所以我们写一个HttpServletRequest的包装类,拦截HttpServletRequest并包装它,传递包装类给访问目标:

过滤器原理和例子_第1张图片

源码:

package cn.xbai.request;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

//继承Java提供的包装类,不用再复写所有Request该有的方法,只需复写自己需要的
public class FilterRequest extends HttpServletRequestWrapper {
	
	//源码跟踪:该类最顶级基类中定义了一个ServletRequest
	//而不是HttpServletRequest,名为request,是被包装对象,
	//可以传给它HttpServletRequest进行初始化,但要用到
	//HttpServletRequest全部功能需要强转(多态)
	/**
	 * 并且该ServletRequest是基类私有,无法在子类调用,所以重新定义,并重写构造方法:
	 */
	private HttpServletRequest request;
	public FilterRequest(HttpServletRequest request) {
		//经测:父类也必须初始化,无论是否利用:
		//除了下面构造函数的原因,经源码跟踪,最重要的:父类根据传入的request获取到它后
		//强转成HttpServletRequest,调用其每个方法实现包装类,如果不传,那么
		//HttpServletRequest的方法根本就无法实现,那么就不是包装类
		//注意这些实现的方法是HttpServletRequest的方法
		//getParameter方法重写:HttpServletRequest是接口类型,调用Web容器传递过来的
		//实现类对象的该方法实现修改
		/**
		 * Implicit super constructor HttpServletRequestWrapper() 
		 * is undefined. Must explicitly invoke another constructor
		 */
		//就是说这个Java提供的包装类没有定义默认构造函数(有非默认,就不会自动创建默认了)
		//那么你不调用它的非默认构造函数,它根本无法初始化,这也是子类无法拥有默认构造函数的原因:
		//子类如果有默认构造函数,里面什么都不写,那么父类缺少一个request参数进行初始化会报错,
		//所以这里继承时就提醒必须有一个调用父类构造函数的子类构造函数并传入request:
		/**
		 * Implicit super constructor HttpServletRequestWrapper() 
		 * is undefined for default constructor. Must define an explicit constructor
		 */
		//并且super初始化要放到第一行
		super(request);
		this.request=request;//但利用的还是子类定义的request
		
		// TODO Auto-generated constructor stub
		/**
		 * 顶级父类最底层:
		 * if (request == null) {
	    throw new IllegalArgumentException("Request cannot be null");   
	}
	this.request = request;
    }
		 */
	}

	//重写其getParameter方法
	@Override
	public String getParameter(String name) {
		//注意:http传递给request的参数无论什么都是String类型!
		//这和我们自己设置的Argument不同!所以:
		String param=request.getParameter(name);//直接调用形参
		
		//同样是HttpServletRequest类,不用判断对象是否为null,是null自然空指针异常
		if(!request.getMethod().equalsIgnoreCase("GET")){
			return param;
		}
		//别忘了return null!
		if(param==null){
			return null;
		}
		
		//注意:不一定是UTF-8,而是调用request的编码
		try {
			return param=new String(param.getBytes("iso-8859-1"),request.getCharacterEncoding());
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);//有返回类型的方法,这里不抛异常也会报错
		}
		
	}
	
	

}


2.Filter

注意可以通过init方法传递过来的FilterConfig对象接收一些参数,包括设置字符集参数,但Filter中要设置一个字段接收

web 应用程序启动时web 服务器将创建Filter 的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(注:filter对象只会创建一次,init方法也只会执行一次。

注意Filter的接收参数里,request,response都不是Http...但这里我们知道是Http请求和响应,所以第一步需要先强转成此类型:

package cn.xbai.filter;

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;

import cn.xbai.request.FilterRequest;

public class FilterDemo implements Filter {
	//为了获取config参数,这里定义一个来接收:
	private FilterConfig config;
	//默认字符集
	private String defaultCharset="UTF-8";
	//这里接收:
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		this.config=filterConfig;
	}
	public void destroy() {
		// TODO Auto-generated method stub

	}
	//注意它的参数不是Http...类型,第一步需要先转成这种类型!!
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("Hi,Filter");
		// TODO Auto-generated method stub
		//获取要设置的字符集------->不写死,写在filter配置中,这里获取
		//设置缺省的,防止配置中未设置
		String charset=this.config.getInitParameter("charset");
		if(charset==null){
			charset=defaultCharset;
		}
		
		//知道服务器传递过来的是Http...这里先转成...
		HttpServletRequest request=(HttpServletRequest) req;
		HttpServletResponse response=(HttpServletResponse) resp;
		
		request.setCharacterEncoding(charset);
		//有时候只设置上面一行,因为mvc模式都是转给jsp输出,Servlet不用来输出,jsp页面已经设置了码表,所以不用在这里设置Servlet的response输出!!
		response.setCharacterEncoding(charset);
		response.setContentType("text/html;charset="+charset);
		//关于get乱码问题:将request包装,修改getParameter方法
		//然后这里将包装后的request传递过去:
		chain.doFilter(new FilterRequest(request), response);
		System.out.println("Hi,Filter Return");
	}


}


3.Servlet

package cn.xbai.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FilterServlet extends HttpServlet {
	//在浏览器地址栏传入中文GET参数,测试过滤器包装过request后的中文传输效果:
	//
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("Hello,Me after filter and before filter return");
		String value=request.getParameter("TestCharacter");
		response.getWriter().write(value);//response也设置了字符集,这里直接输出中文看看效果
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}


4.测试:浏览器地址栏传参:GET方式测试过滤器是否靠谱

在Filter的doFilter前后,以及Servlet中分别输出一句话,看Filter是否针对指定Servlet拦截,看Filter的两次调用时机(链式):

浏览器地址栏访问:(参数GET方式传递)

http://localhost:8080/practice_workday186/servlet/FilterServlet?TestCharacter=中国!中国!

(直接粘贴变成http://localhost:8080/practice_workday186/servlet/FilterServlet?TestCharacter=%E4%B8%AD%E5%9B%BD%EF%BC%81%E4%B8%AD%E5%9B%BD%EF%BC%81)

结果:

输出:中国!中国!

控制台输出:

Hi,Filter
Hello,Me after filter and before filter return
Hi,Filter Return

你可能感兴趣的:(过滤器原理和例子)