2 Servlet规范中的过滤器-Filter
2.1 过滤器入门
2.1.1 过滤器概念及作用
过滤器——Filter,它是JavaWeb三大组件之一。另外两个是Servlet和Listener。
它是在2000年发布的Servlet2.3规范中加入的一个接口。是Servlet规范中非常实用的技术。
它可以对web应用中的所有资源进行拦截,并且在拦截之后进行一些特殊的操作。
常见应用场景:URL级别的权限控制;过滤敏感词汇;中文乱码问题等等。
过滤器介绍
在程序中访问服务器资源时,当一个请求到来,服务器首先判断是否有过滤器与请求资源相关联,如果有,过滤器可以将请求拦截下来,完成一些特定的功能,再由过滤器决定是否交给请求资源。如果没有则像之前那样直接请求资源了,响应也是类似的。
Filter介绍
Filter是一个接口。如果想实现过滤器的功能,必须实现该接口
核心方法
返回值 | 方法名 | 作用 |
void | init(FilterConfig config) | 初始化方法 |
void | doFilter(ServletRequest request,ServletResponse,FilterChain chain) | 对请求资源和响应资源过滤 |
void | destroy() | 销毁方法 |
配置方式
注解方式
配置文件
FilterChain介绍
FilterChain是一个接口,代表过滤器链对象。由Servlet容器提供实现类对象
过滤器可以定义很多个,就会组成过滤器链
如果有多个过滤器,在第一个过滤器中调用下一个过滤器,以此类推。直到到达最终访问资源
如果只有一个过滤器,放行时,就会直接到达最终访问资源
2.1.2 过滤器的入门案例
需求说明
通过Filter过滤器解决多个资源写出中文乱码的问题。
最终目的
通过本需求,最终掌握Filter过滤器的使用。
1)前期准备
创建JavaWeb工程
创建两个Servlet功能类,都向客户端写出中文数据
创建一个Filter过滤器实现类,重写doFilter核心方法
在方法内解决中文乱码,并放行
部署并启动项目
通过浏览器测试
处理中文乱码问题(过滤器拦截 处理完在放行)
先创建两个Servlet
ServletDemo01
package com.itheima.servlet; 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("/servletDemo01") public class ServletDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletDemo01执行了。。。"); //resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("servletDemo01执行了。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
ServletDemo02
package com.itheima.servlet; 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("/servletDemo02") public class ServletDemo02 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletDemo02执行了。。。"); //resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("servletDemo02执行了。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
过滤器
FilterDemo01
package com.itheima.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; /* * 过滤器的基本使用 * */ @WebFilter("/*") public class FilterDemo01 implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo01执行了"); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } }
运行结果如图
控制台输出如下
过滤器使用细节
配置方式
注解方式@WebFilter(拦截路径)
配置文件方式
<!--配置过滤器--> <filter> <filter-name>filterDemo1</filter-name> <filter-class>com.itheima.web.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>filterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
多个过滤器使用顺序
如果有多个过滤器,取决于过滤器映射的顺序
<filter-mapping>
的配置前后顺序决定过滤器的调用顺序,也就是由映射配置顺序决定。
过滤器生命周期
创建
当应用加载时实例化对象并执行init 初始化方法
服务
对象提供服务的过程,执行doFilter方法
销毁
当应用卸载时或服务器停止时对象销毁,执行destory方法。
package com.itheima.filter; import javax.servlet.*; import java.io.IOException; /* * 过滤器的生命周期 * */ //@WebFilter("/*") public class FilterDemo03 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("filterDemo03执行了"); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { System.out.println("对象销毁了"); } }
最后停止服务器的时候销毁
2.2.4 过滤器与Servlet的区别
方法/类型 | Servlet | Filter | 备注 |
---|---|---|---|
初始化 方法 | void init(ServletConfig); |
void init(FilterConfig); |
几乎一样,都是在web.xml中配置参数,用该对象的方法可以获取到。 |
提供服务方法 | void service(request,response); |
void dofilter(request,response,FilterChain); |
Filter比Servlet多了一个FilterChain,它不仅能完成Servlet的功能,而且还可以决定程序是否能继续执行。所以过滤器比Servlet更为强大。 在Struts2中,核心控制器就是一个过滤器。 |
销毁方法 | void destroy(); |
void destroy(); |
FilterConfig介绍
FilterConfig是一个接口,代表过滤器的配置对象,可以加载一些初始化参数。
FilterDemo04
package com.itheima.filter; import javax.servlet.*; import java.io.IOException; import java.net.Socket; /* * 过滤器的配置对象的使用 * */ //@WebFilter("/*") public class FilterDemo04 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("对象初始化成功了。。。"); //获取过滤器名称 String filterName = filterConfig.getFilterName(); System.out.println(filterName); //根据name获取value String username = filterConfig.getInitParameter("username"); System.out.println(username); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo04执行了"); //处理乱码 servletResponse.setContentType("text/html;charset=UTF-8"); //放行 filterChain.doFilter(servletRequest,servletResponse); } }
web.xml
<filter> <filter-name>filterDemo04</filter-name> <filter-class>com.itheima.filter.FilterDemo04</filter-class> <init-param> <param-name>username</param-name> <param-value>zhangsan</param-value> </init-param> </filter> <filter-mapping> <filter-name>filterDemo04</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器五种拦截行为
我们的过滤器目前拦截的是请求,但是在实际开发中,我们还有请求转发和请求包含,以及由服务器触发调用的全局错误页面。默认情况下过滤器是不参与过滤的,要想使用,需要我们配置。配置的方式如下:
拦截方式
<!--配置过滤器--> <filter> <filter-name>FilterDemo1</filter-name> <filter-class>com.itheima.web.filter.FilterDemo1</filter-class> <!--配置开启异步支持,当dispatcher配置ASYNC时,需要配置此行--> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/ServletDemo1</url-pattern> <!--过滤请求:默认值。--> <dispatcher>REQUEST</dispatcher> <!--过滤全局错误页面:当由服务器调用全局错误页面时,过滤器工作--> <dispatcher>ERROR</dispatcher> <!--过滤请求转发:当请求转发时,过滤器工作。--> <dispatcher>FORWARD</dispatcher> <!--过滤请求包含:当请求包含时,过滤器工作。它只能过滤动态包含,jsp的include指令是静态包含--> <dispatcher>INCLUDE</dispatcher> <!--过滤异步类型,它要求我们在filter标签中配置开启异步支持--> <dispatcher>ASYNC</dispatcher> </filter-mapping>
2.3过滤器的使用案例
2.3.2 特殊字符过滤器
1)需求说明
在实际开发中,可能会面临一个问题,就是很多输入框都会遇到特殊字符。此时,我们也可以通过过滤器来解决。
例如:
我们模拟一个论坛,有人发帖问:“在HTML中表示水平线的标签是哪个?”。
如果我们在文本框中直接输入<hr/>
就会出现一条水平线,这个会让发帖人一脸懵。
我们接下来就用过滤器来解决一下。
2)编写步骤
第一步:创建JavaWeb工程
沿用第一个案例的工程
第二步:编写Servlet和JSP
package com.itheima.servlet; 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("/servletDemo01") public class ServletDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String content = req.getParameter("content"); resp.getWriter().write(content); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
jsp页面
<%-- Created by IntelliJ IDEA. User: 12270 Date: 2021/3/22 Time: 22:24 To change this template use File | Settings | File Templates. --%> <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <html> <head> <title>$Title$</title> </head> <body> <form action="${pageContext.request.contextPath}/servletDemo01" method="get"> 回帖:<textarea rows="5" cols="25" name="content"></textarea><br/> <input type="submit" value="发言"> </form> </body> </html>
第三步:编写过滤器
package com.itheima.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class HTMLFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } //创建一个自己的Request类 MyHttpServletRequest2 myrequest = new MyHttpServletRequest2(request); //放行: chain.doFilter(myrequest, response); } public void destroy() { } } class MyHttpServletRequest2 extends HttpServletRequestWrapper { //提供一个构造方法 public MyHttpServletRequest2(HttpServletRequest request){ super(request); } //重写getParameter方法 public String getParameter(String name) { //1.获取出请求正文: 调用父类的获取方法 String value = super.getParameter(name); //2.判断value是否有值 if(value == null){ return null; } return htmlfilter(value); } private String htmlfilter(String message){ if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuilder result = new StringBuilder(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }
第四步:配置过滤器
<?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>HTMLFilter</filter-name> <filter-class>com.itheima.filter.HTMLFilter</filter-class> </filter> <filter-mapping> <filter-name>HTMLFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
测试效果
2.3.2 特殊字符过滤器
1)需求说明
在实际开发中,可能会面临一个问题,就是很多输入框都会遇到特殊字符。此时,我们也可以通过过滤器来解决。
例如:
我们模拟一个论坛,有人发帖问:“在HTML中表示水平线的标签是哪个?”。
如果我们在文本框中直接输入<hr/>
就会出现一条水平线,这个会让发帖人一脸懵。
我们接下来就用过滤器来解决一下。
2)编写步骤
第一步:创建JavaWeb工程
沿用第一个案例的工程
第二步:编写Servlet和JSP
/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class ServletDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String content = request.getParameter("content");
response.getWriter().write(content);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.itheima.web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>
<%page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title></title>
</head>
<body>
<form action="${pageContext.request.contextPath}/ServletDemo1" method="POST">
回帖:<textarea rows="5" cols="25" name="content"></textarea><br/>
<input type="submit" value="发言">
</form>
</body>
</html>
第三步:编写过滤器
/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class HTMLFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
//创建一个自己的Request类
MyHttpServletRequest2 myrequest = new MyHttpServletRequest2(request);
//放行:
chain.doFilter(myrequest, response);
}
public void destroy() {
}
}
class MyHttpServletRequest2 extends HttpServletRequestWrapper {
//提供一个构造方法
public MyHttpServletRequest2(HttpServletRequest request){
super(request);
}
//重写getParameter方法
public String getParameter(String name) {
//1.获取出请求正文: 调用父类的获取方法
String value = super.getParameter(name);
//2.判断value是否有值
if(value == null){
return null;
}
return htmlfilter(value);
}
private String htmlfilter(String message){
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
第四步:配置过滤器
<filter>
<filter-name>HTMLFilter</filter-name>
<filter-class>com.itheima.web.filter.HTMLFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HTMLFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>