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请求(生产环境推荐)
- 在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>
- 在HTML头部添加CSRF头名称的meta标签:
<head> <title>Update user</title> <meta name="_csrf_header" th:content="${_csrf.headerName}"> <script src="/js/updateUserHandler.js"></script> </head>
- 修改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
相关产品推荐
相关产品推荐

