SpringMVC的HiddenHttpMethodFilter详解

本文详细解析了HiddenHttpMethodFilter在Web开发中的作用,主要针对RESTful API中浏览器仅支持GET和POST请求的局限性。HiddenHttpMethodFilter通过拦截POST请求,读取隐藏参数 `_method`,将其转换为PUT、DELETE等HTTP方法,从而实现对PUT和DELETE操作的支持。源码分析中,重点在于doFilterInternal方法,通过创建HttpMethodRequestWrapper包装器,替换原始请求的方法,确保后续处理能够正确执行相应HTTP操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在此之前首先要将一下为什么需要使用HiddenHttpMethodFilter:
Ⅰ、 因为我们在开发中已经规范了一种风格就是MVC的REST风格,它拥有四种请求,分别是:GET、POST、PUT、DELETE。分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
Ⅱ、通常的浏览器却只支持get和post请求,这个时候就需要用到我们的HiddenHttpMethodFilter 过滤器来将post请求转换为put跟delete请求。

HiddenHttpMethodFilter源码:(上来看见源码就头大话,可以直接跳过这直接看第一点即可,一步一步讲解的,如有错误欢迎大牛们指正)

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;
        }
    }
}

  1. 我们首先要知道它是一个Filter,与CharacterEncodingFilter一样继承了OncePerFilter,所以我们最重要的就是关注的它的doFilterInternal方法
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);
    }
  1. 我们首先关注第二行的HttpServletRequest requestToUse = request;新建requestToUse是为了替换原有的request做准备。

  2. 条件if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null)进入条件是方法必须为"post",后面一个条件为无异常。

  3. 获取请求方法String paramValue = request.getParameter(this.methodParam);通过此行代码获取到请求的方式,如:PUT、DELETE、PATCH。方式是通过自己set方法设置:public void setMethodParam(String methodParam) { Assert.hasText(methodParam, "'methodParam' must not be empty"); this.methodParam = methodParam; }

  4. 替换请求方法

if (StringUtils.hasLength(paramValue)) {
     String method = paramValue.toUpperCase(Locale.ENGLISH);
     if (ALLOWED_METHODS.contains(method)) {
         requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
     }
}

① 如果method不为null,就将method参数全部转成大写。String method = paramValue.toUpperCase(Locale.ENGLISH);

② 接着判断是否是ALLOWED_METHODS(PUT、DELETE、PATCH,可以去看全部源码静态代码块中的内容就是其值)常量其中一种:ALLOWED_METHODS.contains(method)

③ 如果前两者条件都满足了就开始替换request请求。
a.调用自己的内部类(HttpMethodRequestWrapper)的构造器,将request和method传入,通过反射的方式获取其methodrequestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);

b.因为内部类(HttpMethodRequestWrapper)重写了getMethod方法,返回的是你设置传入的method(就一个字:

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

c.此时requestToUse已经是你设置的request方法了(PUT、DELETE、PATCH)

  1. 放行filterChain.doFilter((ServletRequest)requestToUse, response);
    将新的requestToUse代替原有的那个request执行后面的代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值