【SpringBoot的坑】Restful请求报错Request method 'POST' not supported,HiddenHttpMethodFilter无法将POST转换为PUT原因分析
直接上结论:
因为 SpringBoot 版本原因,在我目前使用的 2.2.4 版本中,需要在springapplication.xml
文件中 添加配置:
spring.mvc.hiddenmethod.filter.enabled = true
什么是 REST
Restful 目的只是让 url 看起来更简洁实用,是资源状态的一种表达。
Restful 的使用
由于 H5 的 form 表单仅支持 POST 和 GET 两种请求,实现 restfulAPI 还需要 PUT 和 DELETE ,所以需要使用 HiddenHttpMethodFilter
组件在发送HTTP请求时对请求类型进行伪装。
<!--需要区分是员工修改还是添加;表单页面只支持get和post方式,所以不能直接把method="post"改成method="put"-->
<form th:action="@{/emp}" method="post"><!--发送 put请求修改员工数据--><!--1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的,所用是将请求转成我们指定的方式)2、页面创建一个post表单3、创建一个input项,name="_method";值就是我们指定的请求方式--><!--这是一个添加、修改二合一页面,判断如果是修改,才显示这个input标签 --><!--此隐藏域可以被HiddenHttpMethodFilter所处理,然后分发到不同的HttpMethod的处理器上--><input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
前端处理
表单页面只支持get
和post
方式,所以页面创建一个post
表单,表单里面创建一个input
项,name="_method"
,值就是我们指定的请求方式。
后台处理
后台需要用 HiddenHttpMethodFilter
,该组件 SpringBoot 已经自动配置好,无需再 @Bean
组件
错误分析
今天在学 SpringBoot 的时候遇到了由于 starter 版本 1 和 2 不同造成的小坑:
后台总是无法映射到 Controller
里对应的 PUT
请求,报错:
后台报错:Request method ‘POST’ not supported
EmployeeController.java 的部分代码如下:
/*** 员工删除* @param id* @return*/@DeleteMapping("/emp/{id}") // 处理 delete 请求public String deleteEmployee(@PathVariable("id") Integer id) {System.out.println("删除员工id:" + id);employeeDao.delete(id);return "redirect:/emps";}
一开始以为是前端页面的问题,F12 看了一下 HTTP 请求头和表单提交信息:
看上图中显示的请求信息,提交的是post请求,表单中的_method
属性值为delete
,没啥问题
然后去瞄了眼 webmvc 的自动配置类 WebFluxAutoConfiguration:
(路径是org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java
)
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();
}
发现较先前版本多了@ConditionalOnProperty
的注解,也就是条件引入。查看括号内的内容可以知道,这个组件是否加入容器决定于这个属性,再看下SpringBoot配置的metadata元数据对这个property的说明:
(路径是C:\Users\Bug\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.2.4.RELEASE\spring-boot-autoconfigure-2.2.4.RELEASE.jar!\META-INF\spring-configuration-metadata.json
)
{"name": "spring.mvc.hiddenmethod.filter.enabled","type": "java.lang.Boolean","description": "Whether to enable Spring's HiddenHttpMethodFilter.","defaultValue": false
},
可以知道 SpringBoot 仅在spring.mvc.hiddenmethod.filter.enabled
这个 property 的值为 true 时才会引入这个组件,但 SpringBoot 默认该属性值为 false ,所以该版本这个组件是默认没有加入容器的。
所以我们只需在配置文件里加上这一句:spring.mvc.hiddenmethod.filter.enabled = true
即可。
附:Spring 的 HiddenHttpMethodFilter 源码
/** Copyright 2002-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.web.filter;import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;/*** {@link javax.servlet.Filter} that converts posted method parameters into HTTP methods,* retrievable via {@link HttpServletRequest#getMethod()}. Since browsers currently only* support GET and POST, a common technique - used by the Prototype library, for instance -* is to use a normal POST with an additional hidden form field ({@code _method})* to pass the "real" HTTP method along. This filter reads that parameter and changes* the {@link HttpServletRequestWrapper#getMethod()} return value accordingly.* Only {@code "PUT"}, {@code "DELETE"} and {@code "PATCH"} HTTP methods are allowed.** <p>The name of the request parameter defaults to {@code _method}, but can be* adapted via the {@link #setMethodParam(String) methodParam} property.** <p><b>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.</b>* So typically, put a Spring {@link org.springframework.web.multipart.support.MultipartFilter}* <i>before</i> this HiddenHttpMethodFilter in your {@code web.xml} filter chain.** @author Arjen Poutsma* @author Juergen Hoeller* @since 3.0*/
public class HiddenHttpMethodFilter extends OncePerRequestFilter {private static final List<String> ALLOWED_METHODS =Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));/** Default method parameter: {@code _method}. */public static final String DEFAULT_METHOD_PARAM = "_method";private String methodParam = DEFAULT_METHOD_PARAM;/*** Set the parameter name to look for HTTP methods.* @see #DEFAULT_METHOD_PARAM*/public void setMethodParam(String methodParam) {Assert.hasText(methodParam, "'methodParam' must not be empty");this.methodParam = methodParam;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HttpServletRequest requestToUse = request;if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {String paramValue = request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {String method = paramValue.toUpperCase(Locale.ENGLISH);if (ALLOWED_METHODS.contains(method)) {requestToUse = new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter(requestToUse, response);}/*** Simple {@link HttpServletRequest} wrapper that returns the supplied method for* {@link HttpServletRequest#getMethod()}.*/private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;public HttpMethodRequestWrapper(HttpServletRequest request, String method) {super(request);this.method = method;}@Overridepublic String getMethod() {return this.method;}}}
【SpringBoot的坑】Restful请求报错Request method 'POST' not supported,HiddenHttpMethodFilter无法将POST转换为PUT原因分析相关推荐
- shell 获取ora报错信息_ORA-04030: 在尝试分配...字节(...)时进程内存不足的原因分析...
概述 前段时间使用的oracle 11g数据库,在用一段时间后(开始要较长时间才出现,后来较短时间就出现),频繁报ORA-04030错误,具体错误信息是 ORA-04030: 在尝试分配...字节(. ...
- 前端请求报错405 Method Not Allowed
postman 请求可以响应回来数据,但是本地服务调用不了,各种查找原因. 搭建代理服务器,进行配置,导致301直接重定向了,因为后台不允许操作, 原因调用的不是自己的服务器的库,后台没有API对接, ...
- security工作笔记006---oauth2(spring security)报错method_not_allowed(Request method 'GET' not supported)解决
JAVA技术交流QQ群:170933152 最近做智慧城市项目,太恶心了...各种不会,个人负责权限验证中心服务,...唉,慢慢研究吧.. 报错信息 <MethodNotAllowed> ...
- Python——pip安装报错:is not a supported wheel on this platform
pip安装报错:is not a supported wheel on this platform 可能的原因1:安装的不是对应python版本的库,下载的库名中cp35代表python3.5,其它同 ...
- 关于加入@RequestBody后请求报错:Required request body is missing:
关于加入@RequestBody后请求报错: Required request body is missing: 这个错误是由于Controller中加入了@RequestBody后却收不到指定请求体 ...
- springboot接收图片报错 request is not a multipart request 和 multipart boundary was found
后台接收图片报错 request is not a multipart request 和 multipart boundary was found request is not a multipar ...
- 请求报错No route to host (Host unreachable)
请求报错No route to host (Host unreachable); nested exception is java.net.NoRouteToHostException: No rou ...
- ajax请求接口连不上会报错吗_服务端有异常, 导致: Ajax 请求报错 net::ERR_INCOMPLETE_CHUNKED_ENCODING...
服务端有异常, 导致: Ajax 请求报错 net::ERR_INCOMPLETE_CHUNKED_ENCODING 但是,这个 Ajax Http 接口使用浏览器可以直接返回.表明,Ajax 的 G ...
- http请求报错Illegal character in query at index 303的解决方法
http请求报错"Illegal character in query at index 303"的解决方法 执行jmeter的http请求时,请求失败,在Sampler resu ...
最新文章
- 爱奇艺基于SpringCloud的韧性能力建设
- FPGA设计心得(13)aurora的线速率及其用户时钟之间的关系?
- Qt中树形结构显示目录结构
- windbg调试命令2(!gle、g、p)
- 有序数组求中位数问题
- 特征筛选3——卡方检验筛选特征(单变量筛选)
- 【es】 check-rollover-ready read index [alinas-lcc] is not the wtiter index for alians [index-xx]
- 安装easydict
- MySQL数据库中的删除命令:delete、truncate、drop
- 搭建etcd 3.4.15集群(详细教程,包括选举过程、数据备份和恢复)
- 两年经验,尽然斩获多家巨头offer,如:蚂蚁、头条、PingCAP~
- node-mysql_Nodejs与MySQL交互(felixge/node-mysql)
- ps html切图教程,PS网页制作基础教程:学习切图那点儿事
- Linux自学之旅-基础命令(一)
- 抖音神器---python实现图片转字符
- 分享几个音乐下载网站
- juniper防火墙配置
- js 获取汉字首字母和汉字转拼音
- Nginx :user nobody
- 沁恒CH32F103C8T6(二): Linux PlatformIO环境配置, 示例运行和烧录
热门文章
- VIM进阶-模式mode
- C++静态多态(模版模拟多态)的讨论
- 手把手教你玩转SOCKET模型:完成例程(Completion Routine)篇
- C++的查看指针指向空间的所在的区域(是堆还是栈或者静态)和大小
- [转]I,P,B帧和PTS,DTS的关系
- C++ STL : 模拟实现STL中的list类
- Python 实例方法,类方法和静态方法的区别
- QUIC实战(一) 通过Quiche部署支持HTTP3 的NGINX
- 怎么确保网站的可用性
- 畅享音视频技术饕餮盛宴,就在LiveVideoStackCon 上海站