板块一 Servlet编程:第九节 过滤器全解
程序员始终在为代码的可复用性努力着,过滤器也是一个典型的提高可复用性的工具,它让我们在一些逻辑上可以实现一劳永逸的效果
一、什么是过滤器
Filter 即为过滤 ,用于在Servlet之外对Request或者Response进行修改。它主要用于对用户请求进行预处理 ,也可以对HttpServletResponse进行后处理 。
使用Filter的完整流程:Filter 对用户请求进行预处理,接着将请求交给Servlet 进行处理并生成响应,最后Filter再对服务器响应进行后处理。 举个栗子,在 板块一 Servlet编程:第三节Request 中,请求响应进入Servlet的时候,每个文件都要对请求响应作中文乱码的处理,非常难受,如果在数据流入每个Servlet文件前面都经过一个过滤器,就能在过滤器里一劳永逸解决中文乱码问题了
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链
请求时 先配置先执行;响应时 以相反的顺序执行。
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
二、过滤器的实现
在当前包下新建一个Fileter
目录,作过滤器层
在Filter目录下新建filter1.java
,现在的目录结构为
一个过滤器拦截一个目录
在filter1.java中实现一个过滤器需要以下步骤
实例
- 实现接口
- 重写实现生命周期的三个方法
其中init()
、destroy()
显然是我们耳熟能详的过滤器的生命周期的两个方法。而doFilter()
就是过滤器的核心:过滤方法
重写方法后,要在其中添加filterChain.doFilter(servletRequest,servletResponse);
表示过滤后能放行的资源,也就是放行了来时的请求以及去时的响应,否则请求响应无法到达资源
因此总体的doFliter()
方法代码为
java
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("正在经过过滤器");
filterChain.doFilter(servletRequest,servletResponse);
}
- 最后加上过滤器的访问注解
@WebFilter("/start")
注解中的目录就是过滤器将会过滤的目录,当然通常情况下是很多个项目一起过滤,此处只过滤start.java
最终filter.java的代码为
java
package www.caijiyuan.Filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/start")
public class filter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化成功");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("正在经过过滤器");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
它将过滤的start.java文件内容为
java
package www.caijiyuan.Servlt;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/start")
public class start extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("访问到Start");
}
}
启动服务器,在浏览器中访问start,即可得到
可以看出服务器访问start.java文件之前是先经过lfilter1.java过滤器的
多个过滤器(Filter链)拦截一个目录
就如一、什么是过滤器 中图示的,当有多个过滤器时,对于同一个文件请求的拦截顺序和响应的拦截顺序刚好相反,我们用实例来验证
在过滤器层中新添一个filter2
除了文件名内容与filter1完全一致
java
package www.caijiyuan.Filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/start")
public class filter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化成功");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter2正在拦截");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("filter2处理响应");
}
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
此时再启动服务器,在浏览器中访问start.java即可在控制台打印
这就是filter链的演示效果,此外,如果filter1和filter2二者有一没有对请求响应进行filterChain.doFilter
放行就会导致最终无法到达资源
三、过滤器处理中文乱码问题
早在Servlet的request和response两大对象小节我们就已经总结过中文乱码问题,现在我们就假设Tomcat8及以上POST请求时,使用Fliter处理中文乱码问题
实例:通过EncoderFliter.java过滤hello.jsp向after.java传递的中文参数
- 实现生命周期:新建一个EncoderFliter.java的过滤器,
implements
自父类是Servlet包的Filter,并且在类中重写三个生命周期方法init\doFilter\destroy
注意 - 请求响应类型转换:
doFilter
中的request和response对象来自Servlet的实体,而我们之前用的request和response是来自HttpServlet的实体,因此需要在doFilter
方法中类型转换 - 处理乱码
request.setCharacterEncoding("UTF-8");
- 放行资源
filterChain.doFilter(request,response);
- Filter注释标识过滤范围,
"*"
为全部
最终EncoderFliter.java代码为
java
package www.caijiyuan.Filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/*")
public class EncodeFliter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 基于HTTP
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 处理POST请求乱码
request.setCharacterEncoding("UTF-8");
//放行资源
filterChain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
此时的项目结构为
在hello.jsp中写一个POST表单用于传递请求参数
java
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>hello</title>
<h1>Tomcat with IDEA</h1>
</head>
<body>
<form action="after" method="post">
输入参数<input type="text" name="name">
<button>提交</button>
</form>
</body>
</html>
after.java用于接收参数
java
package www.caijiyuan.Servlt;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/after")
public class after extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println(name);
}
}
启动服务器在浏览器中访问hello.jsp,并传中文参
在没有使用过滤器之前,after.java在控制台打印
使用了过滤器之后,after.java在控制台打印
说明参数在被after接受之前确实先经过EncoderFliter.java过滤器,并且成功执行了doFilter
里的设置。
这在我们有大量文件需要处理相同的设置时 尤为方便,可以达到一劳永逸的效果,例如在访问一个购物网站时有些资源是登录以后才能查看的,例如在未登录状态下访问购物车就是一个非法访问,应该跳转到登录界面。这个非法访问拦截操作就可以用过滤器来实现
四、使用XML配置过滤器
除了用Servlet配置一个过滤器,其实我们还可以使用web.xml帮我们配置过滤器一些特定的参数,其基本格式为
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>FilterName</filter-name>
<filter-class>包的路径</filter-class>
<!-- Optional: Filter initialization parameters -->
<init-param>
<param-name>参数名</param-name>
<param-value>参数值</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterName</filter-name>
<url-pattern>/urlPattern</url-pattern>
<!-- Optional: Specify dispatcher types -->
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
</web-app>
例如,我们上面的处理中文乱码的过滤器就可以这样写
EncodeFilter.java
java
package www.caijiyuan.Filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class EncodeFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
// 基于HTTP
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 处理POST请求乱码
request.setCharacterEncoding("UTF-8");
//放行资源
filterChain.doFilter(request,response);
}
}
可以看出少了注解,此时过滤器覆盖的路径可以在web.xml中配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>EncodeFilter</filter-name>
<filter-class>www.caijiyuan.Filter.EncodeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
此时实现的效果与使用Servlet配置过滤器一致
注意,XML的配置必须写在中,若配置XML文件请保证语法配置正确 ,否则Tomcat无法正常启动
可以看到XML中过滤器还有初始参数这一项
xml
<init-param>
<param-name>参数名</param-name>
<param-value>参数值</param-value>
</init-param>
举个栗子可以这样使用:过滤器中使用初始化参数
xml
<filter>
<filter-name>CustomFilter</filter-name>
<filter-class>com.example.CustomFilter</filter-class>
<init-param>
<param-name>apiKey</param-name>
<param-value>YOUR_API_KEY</param-value>
</init-param>
</filter>
我们定义了一个名为CustomFilter的过滤器,并为它指定了一个名为apiKey
的初始化参数,其值为YOUR_API_KEY
在过滤器的init()
方法中,可以通过FilterConfig对象来获取这个初始化参数
java
public class CustomFilter implements Filter {
private String apiKey;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
apiKey = filterConfig.getInitParameter("apiKey");
}
...
}
如此或取了初始化参数的值,并将其存储在apiKey变量中供过滤器的其他方法使用,初始化参数是可选的,可以根据需要在过滤器的配置中添加或删除它们
以上就是此小节的所用内容了,我们学习了过滤器最基础的使用方法,下一节中我们将学习过滤器的兄弟:监听器