用 HiddenHttpMethodFilter 解决浏览器不支持 PUT/DELETE/PATCH 的问题

用 HiddenHttpMethodFilter 解决浏览器不支持 PUT/DELETE/PATCH 的问题

背景

RESTful API 普及后,后端Controller里满是 @PutMapping@DeleteMapping@PatchMapping,写起来很爽。但浏览器只支持 GET 和 POST 两个 HTTP 方法——很多老系统、页面表单、企业内网场景,根本没法发一个真正的 PUT 请求。

改前端代码?后端项目已经上线,改起来牵一发动全身。

Spring 早就想到了这个场景。

HiddenHttpMethodFilter 是什么

HiddenHttpMethodFilter 是 Spring Web 提供的一个 jakarta.servlet.Filter。它的核心逻辑很简单:

  1. 拦截所有 POST 请求
    1. 读取请求体中的 _method 参数
    1. 如果值是 PUTDELETEPATCH,就把请求包装成对应的 HTTP 方法
      浏览器侧看到的永远是 POST,但后端 HttpServletRequest.getMethod() 返回的是真实方法名。

这就是它的唯一功能——转换方法,不做别的

工作原理

过滤器内部维护了一个 HttpMethod 列表,只允许三种方法转换:

private static final List<String> ALLOWED_METHODS =
    List.of("PUT", "DELETE", "PATCH");
    ```
当请求是 POST 且包含 `_method` 参数时,值不在这个列表里就直接忽略,维持 POST 不变。

转换发生在 `HttpServletRequestWrapper` 层——原始请求对象本身没变,是包装类改了 `getMethod()` 的返回值:

```java
// 伪代码逻辑
if ("POST".equals(request.getMethod()) && methodParam != null) {
    String realMethod = request.getParameter(methodParam); // 默认取 _method
        if (ALLOWED_METHODS.contains(realMethod)) {
                return new HttpMethodRequestWrapper(request, realMethod);
                    }
                    }
                    ```
这样后续的 Spring 路由匹配(`@PutMapping` 等注解)能正常工作,因为它们看到的就是真实的 HTTP 方法名。

## 配置方法

### Spring Boot 3.x

Spring Boot 3 使用的是 Jakarta EEFilter 从 `javax.servlet` 换到了 `jakarta.servlet`,其余一样:

```java
@Configuration
public class WebConfig {

    @Bean
        public FilterRegistrationBean<HiddenHttpMethodFilter> hiddenHttpMethodFilter() {
                FilterRegistrationBean<HiddenHttpMethodFilter> bean = new FilterRegistrationBean<>();
                        bean.setFilter(new HiddenHttpMethodFilter());
                                // 可选:改参数名,默认是 _method
                                        // bean.addUrlPatterns("/*");
                                                return bean;
                                                    }
                                                    }
                                                    ```
如果用 Spring Boot 自动配置,什么都不用配,Filter 默认已经启用。

### Spring MVC XML 配置(旧项目)

```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>
                ```
### 前端表单配合

```html
<!-- 发送 PUT 请求 -->
<form action="/users/1" method="POST">
    <input type="hidden" name="_method" value="PUT" />
        <input type="text" name="name" value="张三" />
            <button type="submit">提交</button>
            </form>
<!-- 发送 DELETE 请求 -->
<form action="/users/1" method="POST">
    <input type="hidden" name="_method" value="DELETE" />
        <button type="submit">删除</button>
        </form>
        ```
AJAX 场景也一样:

```javascript
// 发送 PUT 请求
fetch('/users/1', {
    method: 'POST',
        headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                    },
                        body: '_method=PUT&name=张三'
                        });
// 发送 DELETE 请求
fetch('/users/1', {
    method: 'POST',
        body: '_method=DELETE'
        });
        ```
## 一个容易踩的坑:Multipart 和 HiddenHttpMethodFilter 的顺序

官方文档里有这么一句:

> NOTE: This filter needs to run after multipart processing in case of a multipart POST request, due to its inherent need for checking a POST body parameter.
意思是:如果 POST 请求是 `multipart/form-data`(文件上传场景),Filter 必须在 `MultipartFilter` 之后执行。因为文件上传的请求体被解析后,`_method` 参数才能从 body 中读到。

Spring Boot 中 Filter 的顺序通过 `FilterRegistrationBean` 的 `setOrder()` 控制。数字越小越靠前:

```java
@Configuration
public class FilterOrderConfig {

    @Bean
        public FilterRegistrationBean<MultipartFilter> multipartFilter() {
                FilterRegistrationBean<MultipartFilter> bean = new FilterRegistrationBean<>();
                        bean.setFilter(new MultipartFilter());
                                bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最先
                                        return bean;
                                            }
    @Bean
        public FilterRegistrationBean<HiddenHttpMethodFilter> hiddenHttpMethodFilter() {
                FilterRegistrationBean<HiddenHttpMethodFilter> bean = new FilterRegistrationBean<>();
                        bean.setFilter(new HiddenHttpMethodFilter());
                                bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); // MultipartFilter 之后
                                        return bean;
                                            }
                                            }
                                            ```
如果顺序反了——`HiddenHttpMethodFilter` 先执行,`_method` 参数还没被解析出来,转换就不生效。

## 支持的方法只有三种

`HiddenHttpMethodFilter` 只支持 `PUT`、`DELETE`、`PATCH`,不支持 `GET`(浏览器本来就能发),也不支持 `OPTIONS` 和 `HEAD`。如果要支持其他方法,需要自己写 Filter 扩展。

## 实际使用场景

这个 Filter 在以下场景特别有用:

- **企业内部老系统**:页面表单对接 RESTful 后端,不需要改后端代码
- - **第三方平台回调**:很多第三方只支持 POST 回调,但业务需要区分 PUT/DELETE
- - **遗留系统改造**:从 SOAP 迁移到 RESTFUL,不动前端的情况下复用现有 POST 表单
## 总结

`HiddenHttpMethodFilter` 是 Spring 生态里一个"润物细无声"的组件。功能单一、配置简单、几乎零成本解决浏览器 HTTP 方法限制的问题。

核心记住两点:
1. **浏览器发 POST + `_method` 参数**,后端拿到的是真实方法
2. 2. **文件上传场景注意 Filter 顺序**,`MultipartFilter` 要在前
大多数 Spring Boot 项目开箱即用,不用配。只有在需要调整参数名(`methodParam`)或处理 multipart 顺序时才需要手动注册。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码绘春秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值