javaWeb之过滤器(Filter)
Servlet技术规范 描述三种技术 : Servlet(服务器小程序) 、Filter(过滤器)、 Listener(监听器)
1 新建类 继承httpServlet
2 重写doGet 和 dopost方法
3 在web.xml中 注册和映射
1. Filter简介
Filter也称之为过滤器,它是Servlet技术中最实用的技术,
例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,
从而实现一些特殊的功能。例如实现URL级别的权限访问控制、
过滤敏感词汇、解决get和post乱码等一些高级功能。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的
Java类实现了这个接口,则把这个java类称之为过滤器Filter。
通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,
对访问的请求和响应进行拦截,如下所示:
2.Filter是如何实现拦截的?
Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源(拦截url)进行拦截后,WEB服务器每次在调用web资源之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行
是否调用目标资源(即是否让用户访问web资源)。
web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
调用目标资源之后,让一段代码执行
Filter运行在服务器端,对服务器端web资源的访问 进行拦截,起到过滤的作用
Servlet API中 定义接口 Filter,用户只需要编写程序实现Filter接口,完成过滤器编写
- Filter快速入门
3.1. 准备工作
hello.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Hello,JSP</h1>
</body>
</html>
// HelloServlet
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().println("<h1>Hello,Servlet</h1>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
3.2. 编写类 实现 Filter接口
/**
* 编写为过滤器,过滤器作用 用来拦截web资源的访问
*
* @author seawind
*
*/
public class Filter1 implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 执行过滤
System.out.println("执行Filter1 ");
// 拦截后,如何让目标资源执行
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
3.3. 在服务器端注Filter(配置拦截哪个web资源—– web.xml
<!-- 注册过滤器 -->
<filter>
<filter-name>Filter1</filter-name>
<filter-class>cn.itcast.filter.Filter1</filter-class>
</filter>
<!-- 配置过滤器去拦截哪个资源 -->
<filter-mapping>
<filter-name>Filter1</filter-name>
<url-pattern>/hello.jsp</url-pattern>
<!-- 拦截/hello是Servlet 路径 -->
<!-- <url-pattern>/hello</url-pattern>-->
<!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
<servlet-name>HelloServlet</servlet-name>
</filter-mapping>
3.4. 客户端访问被拦截目标资源之前,服务器调用Filter的doFilter方法 ,执行过滤
3.5. Filter的doFilter方法中传入 FilterChain, 如果调用FilterChain的doFilter 就会执行目标资源,否则目标资源不会执行
问题1: 每次使用过滤器都需要 实现filter接口, 再去web.xml中配置, 是否很麻烦? 有没有更加简便的方法?
问题2: 如果配置两个过滤器谁先执行,谁后执行? 由什么决定的?
4. FilterChain
多个过滤器组成过滤器链,谁先执行,谁后执行?
在客户端访问服务器web资源时,服务器端为一个web资源,配置多个过滤器拦截 ,这多个过滤器,就会组成过滤器链 FilterChain, 调用FilterChain的doFilter 表示要执行过滤器链下一个资源,如果当前过滤器已经是链上最后一个过滤器,就会执行目标资源
-
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter
- Filter生命周期
我相信在北京大家都有乘坐地铁的经历, 为了安全期间, 我们必须安检部门的检查, 通过后才可以乘坐地铁.
1 乘坐地铁服务 对应 服务器上资源服务
2 安检部门 对应 过滤器
- Filter生命周期
安检部门上岗前, 必须 发放安检设备
每次安检,都有严格的流程
结束安检, 需要回收安检设备
public class AnjianFilter implements Filter {
public AnjianFilter() {
System.out.println("构造方法: 地铁安检部门成立----------------------");
}
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("init: 安检人员上岗前的准备工作----------------------");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter: 每次有人进入地铁必须进行安全检查----------------------" + new Date().toLocaleString());
// 允许通过
chain.doFilter(request, response);
}
public void destroy() {
System.out.println("destroy: 收回安检设备----------------------");
}
}
init(FilterConfig) doFilter(request,response,filterChain) destroy()
1、Filter对象在tomcat服务器启动时 创建,调用init方法 (只会创建一个对象,init方法执行一次)
2、doFilter 每次拦截目标资源时,执行
3、destroy 服务器关闭时执行
请思考:
安检部门 如果在非节假日客流量少时,5个手持安检设备就够了.
但是碰上节假日客流量多时,10个手持安检设备才够用,需要向上级申请.
节假日过后客流量少了,4个手持安检设备就够了,还需要再次向上级申请换回去…
如此频繁的申请,等待通过是否很麻烦,大家有没有好的主意?
6. FilterConfig对象
FilterConfig 作用和 ServletConfig 类似,用来在Filter初始化阶段,将参数传递给过滤器
1、通过 String getInitParameter(String name) 获得过滤器初始化参数
* FilterConfig 提供参数,是Filter类私有参数,Filter2的初始化参数,不能在Filter1 中进行获取
* 配置全局参数, 进行配置,通过ServletContext 获得
7. 过滤器拦截配置
1、如果连接目标资源是一个Servlet,可以选择url和servlet名称两种配置方式
<!-- 拦截/hello是Servlet 路径 -->
<url-pattern>/hello</url-pattern>
<!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
<servlet-name>HelloServlet</servlet-name>
2、url-pattern 和 Servlet中路径写法一样,有三种 : 完全匹配、目录匹配、扩展名匹配
3、指定过滤器所拦截的资源被 Servlet 容器调用的方式
容器调用服务器端资源 有四种方式
REQUEST、FORWARD、INCLUDE、ERROR
- Filter应用
8.1. 案例1:解决post方式获取参数中文乱码的编码过滤器
案例:编写jsp 输入用户名,在Servlet中获取用户名,将用户名输出到浏览器上
// 处理请求post乱码代码
request.setCharacterEncoding("utf-8");
// 设置响应编码集代码`这里写代码片`
response.setContentType("text/html;charset=utf-8");
经常会使用,而过滤器可以在目标资源之前执行,将很多程序中处理乱码公共代码,提取到过滤器中 ,以后程序中不需要处理编码问题
需求: 要求用户输入用户名,显示在页面上
8.1.1. input.jsp
<form action="${pageContext.request.contextPath }/demo1" method="post">
请输入你的大名<input type="text" name="username"/><br/>
<input type="submit" value="提交"/>
</form>
8.1.2. Demo1Serlvet
public class Demo1Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
response.getWriter().println("幸会," + username);
}
}
8.1.3. EncodingFilter
public class EncodingFilter implements Filter {
public EncodingFilter() {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 解决获取参数出现中文乱码
request.setCharacterEncoding("utf-8");
// 解决浏览器出现中文乱码
response.setContentType("text/html;charset=utf-8");
// 放行
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
案例1缺点: 可以解决post方式的乱码, 但是解决get方式的乱码吗? 如果不可以请思考怎么做?
案例2:解决整个项目的乱码
<form action="/day16/demo2Servlet" method="post">
昵称<input type="text" name="nickname"/><br/>
城市<input type="text" name="city"/><br/>
<input type="submit" value="提交"/>
</form>
public class GenericEncodingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 为了能够使用子类的方法,需要向下转型
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
// 使用包装 对方法进行增强
HttpServletRequest myRequest = new MyRequest(httpServletRequest);
// 放行
chain.doFilter(myRequest, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
@Override
public void destroy() {
}
class MyRequest extends HttpServletRequestWrapper{
private HttpServletRequest httpServletRequest;
// 记录是否修改过
private boolean isUpdate = false;
public MyRequest(HttpServletRequest request) {
super(request);
this.httpServletRequest = request;
}
@Override
public Map<String, String[]> getParameterMap() {
// 1 获取请求方式
String method = httpServletRequest.getMethod();
// 2 根据不同方式,做出对应的处理
if("post".equalsIgnoreCase(method)) {
// 设置请求编码集
try {
httpServletRequest.setCharacterEncoding("utf-8");
return httpServletRequest.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}else if("get".equalsIgnoreCase(method)) {
// 先编码,再解码
Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
if(!isUpdate) {
try {
for(Entry<String, String[]> entry : parameterMap.entrySet()){
String[] valueArr = entry.getValue();
for(int i=0; i<valueArr.length; i++) {
valueArr[i] = new String(valueArr[i].getBytes("iso-8859-1"), "utf-8");
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
isUpdate = true;
}
return parameterMap;
}
return super.getParameterMap();
}
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = this.getParameterMap();
String[] valueArr = parameterMap.get(name);
if(valueArr!=null) {
return valueArr[0];
}else {
return null;
}
}
@Override
public String[] getParameterValues(String name) {
return this.getParameterMap().get(name);
}
}
}
==================================================================================================================
过滤器
1、过滤器编写步骤
2、全局编码 过滤器
3、通用get/post乱码过滤器 ———— 理解实现过程 ,保存起来会使用