直接上结论:

因为 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}"/>

前端处理
表单页面只支持getpost方式,所以页面创建一个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原因分析相关推荐

  1. shell 获取ora报错信息_ORA-04030: 在尝试分配...字节(...)时进程内存不足的原因分析...

    概述 前段时间使用的oracle 11g数据库,在用一段时间后(开始要较长时间才出现,后来较短时间就出现),频繁报ORA-04030错误,具体错误信息是 ORA-04030: 在尝试分配...字节(. ...

  2. 前端请求报错405 Method Not Allowed

    postman 请求可以响应回来数据,但是本地服务调用不了,各种查找原因. 搭建代理服务器,进行配置,导致301直接重定向了,因为后台不允许操作, 原因调用的不是自己的服务器的库,后台没有API对接, ...

  3. security工作笔记006---oauth2(spring security)报错method_not_allowed(Request method 'GET' not supported)解决

    JAVA技术交流QQ群:170933152 最近做智慧城市项目,太恶心了...各种不会,个人负责权限验证中心服务,...唉,慢慢研究吧.. 报错信息 <MethodNotAllowed> ...

  4. Python——pip安装报错:is not a supported wheel on this platform

    pip安装报错:is not a supported wheel on this platform 可能的原因1:安装的不是对应python版本的库,下载的库名中cp35代表python3.5,其它同 ...

  5. 关于加入@RequestBody后请求报错:Required request body is missing:

    关于加入@RequestBody后请求报错: Required request body is missing: 这个错误是由于Controller中加入了@RequestBody后却收不到指定请求体 ...

  6. 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 ...

  7. 请求报错No route to host (Host unreachable)

    请求报错No route to host (Host unreachable); nested exception is java.net.NoRouteToHostException: No rou ...

  8. ajax请求接口连不上会报错吗_服务端有异常, 导致: Ajax 请求报错 net::ERR_INCOMPLETE_CHUNKED_ENCODING...

    服务端有异常, 导致: Ajax 请求报错 net::ERR_INCOMPLETE_CHUNKED_ENCODING 但是,这个 Ajax Http 接口使用浏览器可以直接返回.表明,Ajax 的 G ...

  9. http请求报错Illegal character in query at index 303的解决方法

    http请求报错"Illegal character in query at index 303"的解决方法 执行jmeter的http请求时,请求失败,在Sampler resu ...

最新文章

  1. 爱奇艺基于SpringCloud的韧性能力建设
  2. FPGA设计心得(13)aurora的线速率及其用户时钟之间的关系?
  3. Qt中树形结构显示目录结构
  4. windbg调试命令2(!gle、g、p)
  5. 有序数组求中位数问题
  6. 特征筛选3——卡方检验筛选特征(单变量筛选)
  7. 【es】 check-rollover-ready read index [alinas-lcc] is not the wtiter index for alians [index-xx]
  8. 安装easydict
  9. MySQL数据库中的删除命令:delete、truncate、drop
  10. 搭建etcd 3.4.15集群(详细教程,包括选举过程、数据备份和恢复)
  11. 两年经验,尽然斩获多家巨头offer,如:蚂蚁、头条、PingCAP~
  12. node-mysql_Nodejs与MySQL交互(felixge/node-mysql)
  13. ps html切图教程,PS网页制作基础教程:学习切图那点儿事
  14. Linux自学之旅-基础命令(一)
  15. 抖音神器---python实现图片转字符
  16. 分享几个音乐下载网站
  17. juniper防火墙配置
  18. js 获取汉字首字母和汉字转拼音
  19. Nginx :user nobody
  20. 沁恒CH32F103C8T6(二): Linux PlatformIO环境配置, 示例运行和烧录

热门文章

  1. VIM进阶-模式mode
  2. C++静态多态(模版模拟多态)的讨论
  3. 手把手教你玩转SOCKET模型:完成例程(Completion Routine)篇
  4. C++的查看指针指向空间的所在的区域(是堆还是栈或者静态)和大小
  5. [转]I,P,B帧和PTS,DTS的关系
  6. C++ STL : 模拟实现STL中的list类
  7. Python 实例方法,类方法和静态方法的区别
  8. QUIC实战(一) 通过Quiche部署支持HTTP3 的NGINX
  9. 怎么确保网站的可用性
  10. 畅享音视频技术饕餮盛宴,就在LiveVideoStackCon 上海站