- 表单提交发送PUT和DELETe请求
- IDEA模拟HTTP请求
本篇和RESTFul与RESTFul案例这篇文章有很多重复的地方,就当是复习吧。
表单提交发送PUT和DELETE请求REST(Representational State Transfer,表述性状态转移)是一种架构风格,它使用HTTP请求方式动词表示对资源的操作。
具体看下面的例子吧。
- 新建Spring项目:demo3。
新建时添加3个依赖:Lombok、Spring Configuration Processor和SpringWeb。
项目新建好后,自动生成的pom.xml如下所示。
另外,将自动生成的配置文件application.properties重命名为application.yml。
4.0.0 org.springframework.boot spring-boot-starter-parent 2.6.2 com.example demo3 0.0.1-SNAPSHOT demo3 Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok
- com.example.boot下新建控制器类controller.Demo3Controller。
package com.example.boot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Demo3Controller {
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String get(){
return "get user";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String post(){
return "add user";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String put(){
return "update user";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String delete(){
return "delete user";
}
}
- resources.static下新建欢迎页面index.html。
首页
- 启动应用,访问localhost:8080。
点击按钮REST-GET,表单method设置为get,返回“查询用户”,OK;
点击按钮REST-POST,表单method设置为post,返回“新增用户”,OK;
点击按钮REST-PUT,表单method设置为put,返回“新增用户”,NOK;
点击按钮REST-POST,表单method设置为delete,返回“新增用户”,NOK。
由于表单method仅支持get和post方法,如果method设置为其他请求方式,如本例中的put或delete,浏览器会当作get请求方式处理。
要想使用表单发送put或delete请求,需要借助过滤器HiddenHttpMethodFilter,其源码如下,
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
private static final List ALLOWED_METHODS;
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = "_method";
public HiddenHttpMethodFilter() {
}
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
static {
ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
做好以下两点就行:
1)表单的method设置为post。
2)发送给服务器的数据必须包括名为_method,值为put或delete的这么一个数据,可用input[type=“hidden”]实现。
按照以上思路修改index.html,修改后的结果如下,
首页
另外,还有很重要的一点,那就是需要在配置文件application.yml中添加以下内容,以启用HiddenHttpMethodFilter。
spring:
mvc:
hiddenmethod:
filter:
enabled: true
想知道为啥,到spring-boot-autoconfigure-2.6.2.jar的org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration里看看了。
public class WebMvcAutoConfiguration {
public WebMvcAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = {"enabled"}
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
//...
}
需要满足两个条件,OrderedHiddenHttpMethodFilter实例才会添加到容器中 。
1)@ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) ,条件:类路径下有HiddenHttpMethodFilter。
Ctrl+N,输入OrderedHiddenHttpMethodFilter,可找到,所以满足条件。
2) @ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"} ),条件:配置文件中需要配置spring.mvc.hiddenmthod.filter.enable,且为true或者on。
最后,重启应用,查看效果。
刚刚提到,表单要发送put或delete请求,发送给服务器的数据必须包括名为_method,值为put或delete的这么一个数据,通常用来实现。其实,不一定非得name="_method",也可以是name="_mymethod或者其他,也就是说,可以自定义。具体怎么自定义,还得回到HiddenHttpMethodFilter的源码。
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
//...
private String methodParam = "_method";
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
//...
}
在com.example.boot下新建配置类config.MyConfig,如下,
package com.example.boot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@Configuration
public class MyConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_mymethod");
return hiddenHttpMethodFilter;
}
}
同时修改index.html,将name="_method"修改为name=_mymethod,如下。
重启应用,访问localhost:8080,验证结果,符合预期。
IDEA模拟HTTP请求使用IDEA发送HTTP请求,不需要使用HiddenHttpMethodFilter,不信瞧瞧。
首先修改application.yml把HiddenHttpMethodFilter关闭,如下。
spring:
mvc:
hiddenmethod:
filter:
enabled: off
然后,在demo3项目根目录下新建目录http,在http下新建文件demo3.http,内容如下。
GET http://localhost:8080/user Content-Type: application/json ### POST http://localhost:8080/user Content-Type: application/json ### PUT http://localhost:8080/user Content-Type: application/json ### DELETE http://localhost:8080/user Content-Type: application/json
最后,重启应用,发送请求。



