用 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 列表,只允许三种方法转换:

java 复制代码
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 EE,Filter 从 `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 顺序时才需要手动注册。
相关推荐
晚安code1 小时前
Redis 内存淘汰策略实战:什么时候淘汰、怎么选、怎么防缓存击穿
java·redis·缓存
goyeer1 小时前
Liunx日志管理与journalctl
java·linux·运维·服务器·运维开发·信息化·信息化企业管理
tianyu2342 小时前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
Emily156853598705 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
就叫_这个吧9 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡10 小时前
AI 应用系统设计
java·开发语言·人工智能
城管不管10 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅10 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
AI绘画哇哒哒10 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent