用 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 顺序时才需要手动注册。
相关推荐
shehuiyuelaiyuehao44 分钟前
1计算机是如何工作的
java·计算机网络
letisgo51 小时前
JAVA 高级进阶07篇《@Transactional失效的8个场景:代理机制到传播行为》
java·面试·transactional·spring事务·aop动态代理
水域安全老周1 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
三8442 小时前
Java 模板注入(FreeMarker) · 02 · 模板引擎与 FreeMarker 语法
java·开发语言·web安全·freemarker
暮雨哀尘2 小时前
Java 基础练习(一):创建类并调用对象
java·开发语言
想要入门的程序猿2 小时前
回调函数学习
java·网络·学习
泡海椒2 小时前
动态代理核心原理:JQuick-Java接口规则自动生成机制解析
java·开发语言·python
步行cgn3 小时前
Spring 启动报错:BeanFactory not initialized 的原因与解决
java·python·spring
步行cgn3 小时前
Spring 报错:No bean class specified on bean definition 的原因与解决
java·后端·spring
zhangjw343 小时前
第47篇:Java综合实战:电商秒杀系统(高并发场景)
java·开发语言