这一部分我们要介绍Java EE中的Filter。
Filter可以对用户的请求进行预处理,同时也可以对HttpServletResponse进行后处理,是一个典型的处理链,所以Filter也叫做拦截器。Filter也可以像Servlet一样对用户的请求做出响应,但一般我们都会使用Servlet来响应用户,而不是用Filer。
使用Filter的好处:
- Filter可以在HttpServletRequest到达Servlet之前,拦截HttpServletRequest
- Filter可以检查HttpServletRequest,并且修改HttpServletRequest
- 可以在HttpServletResponse到达客户端之前,拦截HttpServletResponse
- Filter可以检查HttpServletResponse,并且修改HttpServletResponse
总结起来,Filter可以帮助我们在用户的请求访问Servlet之前,拦截用户的请求并做出修改,同时也可以帮助我们在服务器的响应到达用户客户端之前,拦截响应并且做出响应的修改。
Filter的种类:
- 用户授权的Filter:负责检查用户请求并且过滤非法请求
- 日志Filter:用来记录用户的请求和服务器的响应
- 能改变XML内容的XSLT Filter等
- 负责解码的Filter:改变响应和请求的编码
- Filter负责拦截多个请求或者响应
创建Filter的步骤:
- 创建一个Filter处理类
- 配置Filter
创建一个Filter处理类就是写好一个类,并且继承Filter,然后在其中的doFilter方法中编写你的Filter所要做的事情。
配置Filter有两种方式,一种方式是在web.xml文件中配置,一种是使用源注解来配置。
下面是一个改变编码的Filter类,它接收到用户的请求或者接收到服务器的响应会对请求或者响应改变他们的编码:
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.annotation.WebFilter;
/**
* Servlet Filter implementation class EncodingFilter
*/
@WebFilter(filterName="encodingFilter",urlPatterns={"/*"})
public class EncodingFilter implements Filter {
/**
* Default constructor.
*/
public EncodingFilter() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
private String str="UTF-8";
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
request.setCharacterEncoding(str);
response.setCharacterEncoding(str);
// pass the request along the filter chain
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
在上面的Filter中我们使用了源注解来配置Filter,我们只需要在类代码段的上面加入下面代码即可:
@WebFilter(filterName="encodingFilter",urlPatterns={"/*"})
上面的代码中urlPatterns表示我们要拦截的用户请求或者响应的url,“/*”表示拦截一切请求或者响应。
或者我们可以在web.xml中加入下面代码来配置Servlet:
<filter>
<!--Filter的名字,相当于@WebFilter中的filterName属性-->
<filter-name>encodingFilter</filter-name>
<!--Filter实现类-->
<filter-class>EncodingFilter</filter-class>
</filter>
<filter-mapping>
<!--Filter名字-->
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter的几个示例:
日志Filter:在这里我们编写一个记录用户请求的Filter,代码如下:
package com.example;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
/**
* Servlet Filter implementation class Test
*/
@WebFilter("/*")
public class Test implements Filter {
/**
* Default constructor.
*/
public Test() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
System.out.println(request.getRemoteAddr());//输出用户ip地址
HttpServletRequest hreq=(HttpServletRequest)request;
System.out.println(hreq.getRequestURI());//输出用户的请求地址
// pass the request along the filter chain
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
拦截未登录用户:大部分web页面我们都需要用户登录才可以访问,而没有登录的我们将让页面跳转到登录页面,让用户登录,下面的Filter实现的就是这样的功能,代码如下:
package com.example;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* Servlet Filter implementation class Test
*/
@WebFilter("/*")
public class Test implements Filter {
/**
* Default constructor.
*/
public Test() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
HttpServletRequest hreq=(HttpServletRequest)request;
HttpSession session=hreq.getSession();
String path=hreq.getServletPath();
String loginPage="/login.jsp";
String proLogin="/proLogin.jsp";
if(session.getAttribute("user")==null&&!path.endsWith(loginPage)&&!path.endsWith(proLogin))
{
request.getRequestDispatcher(loginPage).forward(request, response);
}
else{
// pass the request along the filter chain
chain.doFilter(request, response);
}
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
在上面的代码中我们的判断语句有三个条件:
- 用户没有登录
- 用户当前不在登录页面
- 用户当前不在处理登录的页面
刚开始编写这个Filter的时候,很多人可能认为我们只需要判断用户有没有登录就好,如果没有登录就跳转到登录页面就可以了,但是如果你真的这样做了你的程序可能会报错,比如用户没有登录然后跳转到了登录页面,然后用户输入用户名和密码并且点击了登录,这时候程序会有两个动作,一个是你的登录页面写的当用户登录成功后要跳转到一定的页面,另一个动作是请求被Filter拦截了,这时候Filter也有一个当用户已经登录的时候做的跳转,导致的结果就是你的程序同一时间出现了两个跳转,程序就会报错,所以我们必须要有下面的两个条件,这样才不会导致上述错误的发生。