Servlet 过滤器

一、Filter 概述

Filter 也称之为过滤器,它是Servlet 技术中很实用的一个知识点,Web 开发人员通过Filter 技术,对Web 服务器管理的所有Web 资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

Servlet API中提供了一个Filter 接口,开发Web 应用时,如果编写的Java 类实现了这个接口,则把这个Java 类称之为过滤器Filter。通过Filte r技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

1.1实现过滤器的步骤

  1. 写一个类实现特定的接口Filter
  2. 在web .xml 中注册该过滤器

在配置过滤器的时候有点类似在web .xml 中配置Servlet。主要相关配置如下

     <filter>
        <filter-name>Demo1Filter</filter-name> <!-- 给过滤器起名字-->
        <filter-class>com.jas.filter.Filter1</filter-class> <!-- 过滤器的处理类-->
    </filter>

    <!-- 一个Filter可以配置多个filter-mapping-->
    <filter-mapping> 
        <filter-name>Demo1Filter</filter-name>  <!-- 指定上面过滤器的名字-->

        <!-- 一个Filtermapping中可以配置多个url-partten--> 
        <url-pattern>/servlet/Demo1Servlet</url-pattern>
        <url-pattern>/servlet/*</url-pattern>
        <url-pattern>/*</url-pattern>

        <servlet-name>DemoServlet</servlet-name>  <!-- 具体要拦截哪一个Servlet-->
    </filter-mapping>

二、Filter 接口中相关方法介绍

接口Filter 中一共提供了三个方法,分别是:
init(FilterConfig filterConfig)、
doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 、
destroy()。

2.1init 方法

这个方法中允许接收一个FilterConfig 对象,可以通过这个对象获得在web.xml 中配置当前过滤器的初始化配置信息。

<!-- 可以配置当前过滤器的初始化信息,可以配置多个,在Filter中利用FilterConfig对象来获取--> 
<init-param>
    <param-name>name1</param-name>
    <param-value>value1</param-value>
</init-param>

2.2doFilter 方法

这个方法中接收了三个参数,分别是request、response和chain。前两个参数大家都比较熟悉这里就不再做具体介绍。主要介绍一下FilterChain 对象。

FilterChain : 代表过滤器链的对象
一个资源可能被多个过滤器所拦截到,拦截的顺序和过滤器在web.xml 中filter-mapping 的配置顺序相同。 所有对当前资源访问进行拦截的过滤器按照拦截顺序就组成了一个过滤器链。这个过滤器链的最后一个节点是要访问的资源。FilterChain 提供了doFilter 方法,这个方法一旦被调用就表明当前过滤器没有问题了,直接放行,请求执行过滤器链的下一个节点,如果下一个节点是资源则直接访问该资源。

2.3destroy 方法

这个方法没有什么需要特别注意的地方。在过滤器对象销毁之前,执行相应的操作。

2.4过滤器的生命周期

  1. 当服务器启动时,Web 应用加载后,立即创建这个Web 应用中的所有的过滤器,过滤器创建出来后立即调用init 方法执行初始化的操作。
  2. 创建出来后一直驻留在内存中为后续的拦截进行服务,每次拦截到请求后都会调用doFilter 方法。
  3. 在服务器关闭或Web 应用被移除出容器时,随着Web 应用的销毁,过滤器对象销毁。销毁之前调用destory方法。

三、多个过滤器的执行顺序

3.1示例

通过上面的了解我们知道,当配置多个过滤器时,会构成一个过滤器链,该过滤器链的最后一个节点代表要访问的资源,过滤器的执行顺序是在web.xml 中filter-mapping 中配置的顺序。下面就来通过一个具体例子来实践一下

Filter1

package com.jas.filter;

import javax.servlet.*;
import java.io.IOException;

public class Filter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println("过滤器1 前");
        chain.doFilter(request,response);    //放行到下一个过滤器或要访问的资源
        System.out.println("过滤器1 后");
    }

    @Override
    public void destroy() {}
}

Filter2

package com.jas.filter;

import javax.servlet.*;
import java.io.IOException;

public class Filter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println("过滤器2 前");
        chain.doFilter(request,response);
        System.out.println("过滤器2 后");
    }

    @Override
    public void destroy() {}
}

在web.xml 中注册这两个过滤器

    <filter>
        <filter-name>filter1</filter-name>
        <filter-class>com.jas.filter.Filter1</filter-class>
    </filter>
    <filter>
        <filter-name>filter2</filter-name>
        <filter-class>com.jas.filter.Filter2</filter-class>
    </filter>

    <!-- 配置的顺序:filter1 - > filter2 -->
    <filter-mapping>
        <filter-name>filter1</filter-name>
            <url-pattern>/*</url-pattern>    <!-- 拦截所有请求REQUEST-->
    </filter-mapping>
    <filter-mapping>
        <filter-name>filter2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

index.jsp(在控制台输出一句话)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
      <% System.out.println("要访问的资源");%>  
  </body>
</html>

在访问index.jsp 后,控制台输出如下

过滤器1 前
过滤器2 前
要访问的资源
过滤器2 后
过滤器1 后

3.2总结

对于有多个过滤器拦截的资源,在访问时会按照web.xml 中的filter-mapping的 配置顺序执行,
在执行完 chain.doFilter(request,response) 之后(放行),下面的代码并不会立即执行,
而是在对资源访问结束后,再返回来执行 chain.doFilter(request,response) 下的动作,
这个执行顺序和过滤器的执行顺序是相反的。

四、过滤器应用之解决请求乱码

4.1Servlet 请求与响应乱码总结

PS:Tomcat 默认的编码格式是“iso8859-1”,浏览器一般是”utf-8”。

响应乱码
之所以有乱码的出现,是因为服务器对输出数据的编码与浏览器端对数据的解码使用的不是同一个编码格式。

解决方式一:

response.setCharacterEncoding("utf-8");     //指定服务器对输出数据的编码使用“utf-8”
response.setContentType("text/html;charset=utf-8");     //指定客户端浏览器对数据的解码使用“utf-8”

解决方式二:

response.setContentTye("text/html;charset=utf-8");      //即指定服务器的编码格式也指定浏览器的解码格式

请求乱码
请求乱码的出现是因为服务端对客户端发送的数据的解码格式与客户端编码格式不一致造成的。由于客户端发送数据的方式主要是GET 与 POST 方式,所以对应的乱码解决方式也不一样。

对于POST 方式提交的数据的解决方式:

request.setCharacterEncoding("utf-8");      //明确告知服务器以浏览器的编码格式来处理数据

对于GET 方式提交的数据的解决方式:

String username = request.getParameter("username");
//对于GET 方式提交的数据,不能通过上面的方式解决乱码,只能手动对数据进行编解码
//先按照服务器的编码格式对数据进行编码,编码回原来的二进制文件,接着再以客户端的编码格式utf-8“”对数据进行解析
username = new String(username.getBytes("iso8859-1"),"utf-8");      

过滤器解决请求乱码

针对上面关于GET 方式与POST 方式提交数据的方式以及解决乱码的方式,我们在每个Servlet 里都需要对请求的参数进行编解码。在了解过滤器之后,我们就可以在访问Servlet 之前对编解码格式进行统一的指定,这样就不用在Servlet 里写编解码格式的代码了。

过滤器类EncodingFilter

package com.jas.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.Map;

public class EncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        chain.doFilter(new MyHttpServletRequest((HttpServletRequest) request),response);
    }

    @Override
    public void destroy() { }
}

//HttpServletRequestWrapper 本身是HttpServletRequest 的装饰类
class MyHttpServletRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request = null;
    private boolean flag = true;

    public MyHttpServletRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        try {
            if (request.getMethod().equalsIgnoreCase("post")) {      //处理post表单提交乱码
                request.setCharacterEncoding("utf-8");
                return this.request.getParameterMap();
            } else if (request.getMethod().equalsIgnoreCase("get")) {      //处理get方式请求的乱码
                Map<String, String[]> map = this.request.getParameterMap();    //请求信息组成的map

                if (flag) {         
                    //循环遍历参数map进行转码
                    for (Map.Entry<String, String[]> entry : map.entrySet()) {
                        String[] value = entry.getValue();
                        for (int i = 0; i < value.length; i++) {
                            //进行手动的编解码
                            value[i] = new String(value[i].getBytes("iso8859-1"), "utf-8");
                        }
                    }
                }
                flag = false;    //避免解码正确后再进行解码,出现乱码
                return map;
            } else {        //对于其他方式提交的数据不做处理 -> 一般是GET 与 POST
                return request.getParameterMap();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String getParameter(String name) {
        return getParameterValues(name)[0] == null ? null : getParameterValues(name)[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        return getParameterMap().get(name);
    }
}

在web.xml 中注册该过滤器

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>com.jas.filter.EncodingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值