前言
感谢
Filter介绍
我们在登录网站的时候,经常会遇到这种情况:我们想要对某个信息进行评论,却被告知要先登录。
我们想要看某个电影,读一本什么小说,却要求要开会员,购买等等。
也就是说,不达到一定的条件,这些资源我们是不能进行交互的。
我们之前学习的某些知识也可以实现这些内容,比如对比某个用户的session信息,然后到数据库去查看这些权限等等。但是过于复杂。
接下来我们要讲解的内容叫做过滤器Filter。过滤器可以十分方便的实现这些内容。
在访问服务器上的资源的时候,会首先通过过滤器,假如过滤器要求放行,那我们就要放行,加入过滤器不允许,那就拦截。
不仅于此,过滤器因为是在访问资源之前执行的,还可以在过滤器中统一编码字符集等
那么总结一些过滤器可以做到的事情:
1、登录验证
2、统一编码处理
3、敏感字符过滤
4、其他
快速起步
之前创建一个JavaWeb的项目就不讲了,我们直接上正菜
1、定义一个类,实现接口 javax.servlet.Filter
2、复写方法
package com.howling;
import javax.servlet.*;
import java.io.IOException;
public class HelloFilter implements Filter {
/**
* 初始化,当web服务器启动的时候进行初始化
*
* @param filterConfig
* @throws ServletException
*/
public void init(FilterConfig filterConfig)
throws ServletException {
System.out.println("Filter初始化,随时等待请求....");
}
/**
* 进行过滤操作
* 过滤配置的请求,chain:链
* 让请求继续通行:chain.diFilter(request,response)
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("过滤请求......");
filterChain.doFilter(servletRequest, servletResponse);
}
/**
* 销毁Filter...
*/
public void destroy() {
System.out.println("Filter销毁......");
}
}
其实说老实话,看这个方法名字,作用其实就能猜到差不多,一个好的方法名字就应该是这样的
3、配置拦截路径,可以使用web.xml或者注解的方式
- 使用web.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"
metadata-complete="false">
<filter>
<filter-name>FirstFilter</filter-name>
<!--FirstFilter所对应的全类名-->
<filter-class>com.howling.HelloFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<!--在进行任何请求的时候,都会走过滤器FirstFilter-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 使用注解的方式实现,只需要在过滤器上添加一个注解即可
package com.howling;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class HelloFilter implements Filter {
public void init(FilterConfig filterConfig)
throws ServletException {
System.out.println("Filter初始化,随时等待请求....");
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("过滤请求......");
filterChain.doFilter(servletRequest, servletResponse);
}
public void destroy() {
System.out.println("Filter销毁......");
}
}
不用编写页面,直接随意访问一个路径,看看它是否能够进行过滤即可 当然了,如果你是根据原型创建的框架,现在应该已经有了一个默认的index.jsp
4、进行请求
在控制台中我们可以验证到:在服务器启动的时候Filter就已经进行了初始化,然后在访问资源的时候进行了过滤操作,最后关闭服务器的时候进行了Filter的销毁
拦截器配置详解
拦截请求
1、拦截具体资源:/index.jsp
:只有访问index.jsp时,才会进行拦截
2、拦截目录:/user/*
:只有访问/user/xxx时的资源时,才会进行拦截
3、后缀名拦截:*.jsp*
:只有访问jsp资源时,才会进行拦截
4、拦截所有:/*
:访问所有资源都会拦截
注解拦截
dispatcherTypes:可以设置多个值,这多个值其实是枚举
1、REQUEST:默认值,浏览器直接请求资源 只要发起的请求是一次HTTP请求,就走几次过滤器。重定向相当于走了两次请求 2、FORWARD:转发访问资源 只有当前页面是通过请求转发过来的场景,才会走指定的过滤器 3、INCLUDE:包含访问资源 只要JSP是通过
<jsp:include page="xxx.jsp">
嵌入进来的界面,每嵌入一个界面,都会走一次指定的过滤器 4、ERROR:错误跳转资源 假如在web.xml中配置了<error-page>
这个意思是HTTP的响应状态吗只要是错误码之一,就会将请求转发到error.jsp下,这就触发了一次error,然后就走error过滤器 虽然这个过程是转发到error.jsp的过程,但是并不会走转发过滤器 一般来说,我们一般将404,400,500设为错误码 5、ASYNC:异步访问资源 访问一步资源的时候进行过滤
xml拦截
dispatcherTypes:必须写在filter-mapping的最后
接下来我将使用xml的形式进行配置,但是先不演示异步
1、首先是几个JSP页面
index.jsp
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
include的页面
- A.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:include page="B.jsp"/>
<html>
<head>
<title>A</title>
</head>
<body>
<h1>A</h1>
</body>
</html>
一般来说,一个JSP引入另外一个JSP有两种方式,一种就是我们的指令:
<%@include file="./B.jsp" %>
另一种就是JSP的动作:<jsp:include page="B.jsp"/>
这两种方式还是有区别的 1、第一种方式,是在jsp被转换为Servlet之前将引入的代码插入进去 2、第二种方式,是在JSP在被请求的时候将插入的页面引入进来 所以他们因为调用的时间不同,从而导致第一种方式不能被过滤器识别,只会执行request过滤器动作
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>B</title>
</head>
<body>
<h1>B</h1>
</body>
</html>
转发页面
- redirection.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>redirection</title>
</head>
<body>
<h1>redirection</h1>
</body>
</html>
重定向页面
- repost.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>repost</title>
</head>
<body>
<h1>repost</h1>
</body>
</html>
错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>ERROR</title>
</head>
<body>
<h1>ERROR</h1>
</body>
</html>
2、几个Servlet
package com.howling.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RedirectionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/redirection/redirection.jsp").forward(req,resp);
}
}
package com.howling.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RepostServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/repost/repost.jsp");
}
}
只写了转发和重定向的界面,这个非常合理,因为只有这两个需要Servlet的测试
3、几个过滤器
- 请求过滤器
package com.howling.filter;
import javax.servlet.*;
import java.io.IOException;
public class RequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("requestFilter执行...");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 包含过滤器
package com.howling.filter;
import javax.servlet.*;
import java.io.IOException;
public class IncludeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("includeFilter执行...");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 错误过滤器
package com.howling.filter;
import javax.servlet.*;
import java.io.IOException;
public class ErrorFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("errorFilter执行...");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 转发过滤器
package com.howling.filter;
import javax.servlet.*;
import java.io.IOException;
public class RedirectionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("redirectionFilter执行...");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 重定向过滤器
package com.howling.filter;
import javax.servlet.*;
import java.io.IOException;
public class RepostFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("responseFilter执行...");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
4、web.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"
metadata-complete="false">
<!--配置错误页面-->
<error-page>
<error-code>400</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<!--请求过滤器,浏览器直接请求资源时进行拦截,重定向也算一次请求-->
<!--跳转错误资源时执行过滤器-->
<filter>
<filter-name>error</filter-name>
<filter-class>com.howling.filter.ErrorFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>error</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<!--请求时执行过滤器-->
<filter>
<filter-name>request</filter-name>
<filter-class>com.howling.filter.RequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>request</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<!--转发时执行过滤器-->
<filter>
<filter-name>forward</filter-name>
<filter-class>com.howling.filter.RedirectionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>forward</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<!--包含时执行过滤器-->
<filter>
<filter-name>include</filter-name>
<filter-class>com.howling.filter.IncludeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>include</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!--配置servlet映射-->
<servlet>
<servlet-name>redirectionServlet</servlet-name>
<servlet-class>com.howling.servlet.RedirectionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>redirectionServlet</servlet-name>
<url-pattern>/redirect</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>repostServlet</servlet-name>
<servlet-class>com.howling.servlet.RepostServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>repostServlet</servlet-name>
<url-pattern>/repost</url-pattern>
</servlet-mapping>
</web-app>
5、最后一步,就是发起请求来测试了
为了方便,我把项目的结构贴出来
过滤器链
过滤器执行的顺序为
1、注解配置:按照类名字符串排序,值小的先执行
2、web.xml配置:谁的<filter-mapping>
在上面,谁先执行
举个例子,我在上面的过滤器中,第一个是request,然后是其他。 假设现在我们要访问一个资源,第一个永远是request,然后才是其他的内容
但是在这里我十分不推荐这种顺序 假如我现在要访问一个不存在的资源,还是会首先执行requestfilter,然后执行errorfilter 假如我的策略是,在执行errorfilter的时候就将这个请求拦截,那么请求requestfilter岂不是多此一举
所以我在使用过滤器的一般原则是:范围越小,针对越独特的放在前面;范围大,适用范围越广的放在后面 当然,这个也要根据具体的业务来判断
过滤器到资源的路径为
现在我们有两个过滤器A和B。假设我们首先执行A,然后执行B
1、访问资源的顺序:A—>B—>资源
2、从资源返回的顺序:资源—>B—>A
比如说:
public class TestFilter2 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("2 before");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("2 after");
}
}
public class TestFilter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("1 before");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("1 after");
}
}
@WebServlet("/test")
public class Test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet...");
resp.getWriter().write("servlet");
}
}
最终结果:
1 before
2 before
servlet...
2 after
1 after