You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中PATCH请求不被允许问题求助

处理HTML表单发送PATCH请求时的405错误排查与解决

问题背景

通过HTML表单发送PATCH请求更新Person信息,后端已提供对应接口,但前端JS重写提交逻辑后返回405 Method Not Allowed错误,而使用curl发送相同请求时服务器能正常响应。


后端PATCH接口代码

@PatchMapping(path = "/person/update", consumes = "application/json")
public ResponseEntity<Person> updatePerson(@RequestBody Person person) {
    try {
        var updatedUser = service.update(person);
        return ResponseEntity.ok(updatedUser);
    } catch (Exception e) {
        // 异常处理
        return null;
    }
}

前端HTML表单

<!DOCTYPE html >
<html lang="en">
<head>
    <title>Update user</title>
    <script src="/js/updateUserHandler.js"></script>
</head>
<body>
<div>
    <form method="post" action="/person/update" th:object="${person}" id="updateUser">
        <nobr>PERSON DATA</nobr>
        <input type="submit" name="save" value="save">
        <input type="text" id="name" name="name" th:value="${person.getName()}">
        <br>
        <nobr>User name:</nobr>
        <input type="text" name="surname" th:value="${person.getSurname()}">
        <br>
    </form>
</div>
</body>
</html>

前端JS提交逻辑

window.addEventListener('load',
    () => {
        const myForm = document.getElementById('updateUser');
        myForm.addEventListener('submit',
            (event) => {
                event.preventDefault();
                const data = new FormData(myForm);

                const json = {};
                data.forEach((value, key) => json[key] = value);
                const jsonStr = JSON.stringify(json);

                const endpoint = `/person/update`;

                fetch(endpoint, { // 此处触发方法不允许的错误
                    method: 'patch',
                    body: jsonStr,
                    headers: {'Content-Type': 'application/json'}
                })            
          })
    })

错误响应

{
    "timestamp": "2023-07-10T18:24:53.019+00:00",
    "status": 405,
    "error": "Method Not Allowed",
    "path": "/person/update"
}

正常工作的curl请求

curl -X PATCH --header "Content-Type: application/json" --header 'Accept: application/json' \
  --data '{"name": "Vanes", "surname": "Kann", "age": "25"}' \
 http://localhost:8080/person/update

原因分析与解决方案

核心原因

Spring Boot默认开启CSRF防护机制,浏览器发起的非GET请求(如PATCH)必须携带CSRF令牌才能通过校验。curl请求未经过浏览器环境,不会触发CSRF校验;而前端fetch请求缺少CSRF令牌,被服务器拦截返回405错误。

解决方案

方案1:添加CSRF令牌到fetch请求(生产环境推荐)

  1. 在HTML表单中添加隐藏的CSRF令牌字段:
<form method="post" action="/person/update" th:object="${person}" id="updateUser">
    <!-- 新增CSRF隐藏字段 -->
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
    <!-- 原有表单内容 -->
    <nobr>PERSON DATA</nobr>
    <input type="submit" name="save" value="save">
    <input type="text" id="name" name="name" th:value="${person.getName()}">
    <br>
    <nobr>User name:</nobr>
    <input type="text" name="surname" th:value="${person.getSurname()}">
    <br>
</form>
  1. 在HTML头部添加CSRF头名称的meta标签:
<head>
    <title>Update user</title>
    <meta name="_csrf_header" th:content="${_csrf.headerName}">
    <script src="/js/updateUserHandler.js"></script>
</head>
  1. 修改JS代码,将CSRF令牌添加到请求头:
window.addEventListener('load',
    () => {
        const myForm = document.getElementById('updateUser');
        myForm.addEventListener('submit',
            (event) => {
                event.preventDefault();
                const data = new FormData(myForm);

                const json = {};
                data.forEach((value, key) => {
                    // 排除CSRF参数,单独放到请求头中
                    if(key !== '_csrf') json[key] = value;
                });
                const jsonStr = JSON.stringify(json);

                // 获取CSRF令牌和头名称
                const csrfToken = data.get('_csrf');
                const csrfHeaderName = document.querySelector('meta[name="_csrf_header"]').content;

                const endpoint = `/person/update`;

                fetch(endpoint, {
                    method: 'PATCH', // 统一大写更规范
                    body: jsonStr,
                    headers: {
                        'Content-Type': 'application/json',
                        'Accept': 'application/json', // 添加与curl一致的Accept头
                        [csrfHeaderName]: csrfToken
                    }
                })
                // 新增响应处理逻辑
                .then(response => {
                    if(response.ok) return response.json();
                    throw new Error('更新请求失败');
                })
                .then(result => console.log('更新成功:', result))
                .catch(err => console.error('错误信息:', err));
          })
    })

方案2:临时关闭CSRF防护(仅测试环境用)

在Spring配置中关闭CSRF防护:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }
}

或者在application.properties中添加:

spring.security.csrf.enabled=false

内容的提问来源于stack exchange,提问作者verabezf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:03:11