javascript
【SpringBoot的坑】Restful请求报错Request method 'POST' not supported,HiddenHttpMethodFilter无法将POST转换为PUT原因分析
直接上結(jié)論:
因為 SpringBoot 版本原因,在我目前使用的 2.2.4 版本中,需要在springapplication.xml文件中 添加配置:
spring.mvc.hiddenmethod.filter.enabled = true什么是 REST
Restful 目的只是讓 url 看起來更簡潔實用,是資源狀態(tài)的一種表達。
Restful 的使用
由于 H5 的 form 表單僅支持 POST 和 GET 兩種請求,實現(xiàn) restfulAPI 還需要 PUT 和 DELETE ,所以需要使用 HiddenHttpMethodFilter 組件在發(fā)送HTTP請求時對請求類型進行偽裝。
<!--需要區(qū)分是員工修改還是添加;表單頁面只支持get和post方式,所以不能直接把method="post"改成method="put"--> <form th:action="@{/emp}" method="post"><!--發(fā)送 put請求修改員工數(shù)據(jù)--><!--1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自動配置好的,所用是將請求轉(zhuǎn)成我們指定的方式)2、頁面創(chuàng)建一個post表單3、創(chuàng)建一個input項,name="_method";值就是我們指定的請求方式--><!--這是一個添加、修改二合一頁面,判斷如果是修改,才顯示這個input標簽 --><!--此隱藏域可以被HiddenHttpMethodFilter所處理,然后分發(fā)到不同的HttpMethod的處理器上--><input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>前端處理
表單頁面只支持get和post方式,所以頁面創(chuàng)建一個post表單,表單里面創(chuàng)建一個input項,name="_method",值就是我們指定的請求方式。
后臺處理
后臺需要用 HiddenHttpMethodFilter,該組件 SpringBoot 已經(jīng)自動配置好,無需再 @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)
?發(fā)現(xiàn)較先前版本多了@ConditionalOnProperty的注解,也就是條件引入。查看括號內(nèi)的內(nèi)容可以知道,這個組件是否加入容器決定于這個屬性,再看下SpringBoot配置的metadata元數(shù)據(jù)對這個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)
可以知道 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;}}}總結(jié)
以上是生活随笔為你收集整理的【SpringBoot的坑】Restful请求报错Request method 'POST' not supported,HiddenHttpMethodFilter无法将POST转换为PUT原因分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【SpringBoot】使用Maven添
- 下一篇: 【Docker】Docker操作常用命令