IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> SpringMVC过滤器HiddenHttpMethodFilter内部执行流程 -> 正文阅读

[网络协议]SpringMVC过滤器HiddenHttpMethodFilter内部执行流程

SpringMVC过滤器HiddenHttpMethodFilter内部执行流程

@TOC

基于RESTful风格,我们主要将请求方式分为了四种方式,具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

它们分别对应四种基本操作:

  • GET 用来获取资源

  • POST 用来新建资源

  • PUT 用来更新资源

  • DELETE 用来删除资源

大家都知道,form表单的提交方式只有两种:get和post。

而我们现在需要发送四种请求,前两种还好说,因为本来就支持,但是对于put和delete请求。如果直接发送,自然是不可能的。那么如何实现呢?

SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求。

注册配置

因此需要我们在web.xml文件中进行注册配置:

<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <!--过滤的请求路径-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

除了添加此配置外,在处理put和delete请求的时候,还需要我们满足以下条件:

  • 当前请求方式必须为post
  • 当前请求必须传输请求参数_method

满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数 _method 的值,因此请求参数 _method的值才是最终的请求方式。

为什么需要满足这两个条件呢?通过分析源码我们能很清楚的了解到执行流程。

通过源码分析执行流程

首先,既然是过滤器,那么肯定实现了ServletAPI提供给的接口Filter:

image-20220307163235289

他直接继承自GenericFilterBean类,而查看此类我们发现,

image-20220307163347938

他又实现了Filter接口,因此实际上HiddenHttpMethodFilter就是Filter的一个实现类。

知道了这点就好办了,我们知道,所有的过滤器都包含三部分:拦截,处理,放行

而最主要的就是doFilter()方法,也就是我们的处理操作。

对应的HiddenHttpMethodFilter的源码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.web.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    private static final List<String> ALLOWED_METHODS;
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";

    public HiddenHttpMethodFilter() {
    }

    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest requestToUse = request;
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                if (ALLOWED_METHODS.contains(method)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                }
            }
        }

        filterChain.doFilter((ServletRequest)requestToUse, response);
    }

    static {
        ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    }

    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }

        public String getMethod() {
            return this.method;
        }
    }
}

接下来通过行号分析流程:

  • 首先,请求被HiddenHttpMethodFilter拦截,并执行了处理方法doFilterInternal(),第36行。所进行的处理也就基于此方法。

  • 一上来就直接创建了一个新的HTTPServletRequest对象,用于做最后的放行传递,那么肯定是要对这个对象就行加工的,接着往下看

  • 38行,获取当前请求方式并判断是否是POST请求,如果是则向下执行,不是直接放行,因此如果我们想要处理put或者delete请求,就必须要求我们的请求方式为post

  • 接下来就是获取this.methodParam对应的参数值,那这个this.methodParam是什么呢?第26行,我们得知为_method,也就是为什么当前请求必须传输请求参数_method

  • 判断如果不为空时,则将参数转换为大写,然后判断ALLOWED_METHODS中是否包含此参数值,通过24行我们知道ALLOWED_METHODS是一个String类型的List集合,在52行,对他进行了赋值,存放了三个枚举类型:

    image-20220307165942777

    正好对应着我们需要处理的请求方式,因此我们需要将_method参数值设置为我们想要转换的请求,即put或者delete

  • 最后又通过内部包装类HttpMethodRequestWrapper进行包装,最后放行。

注:

我们在配置注册时,往往也需要配置编码过滤器CharacterEncodingFilter,而如果我们两个都配置时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

原因:

  • 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的

  • request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作

  • 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:

  • String paramValue = request.getParameter(this.methodParam);
    
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:56:03  更:2022-03-08 22:56:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:43:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码