REST使用与原理

本篇和RESTFul与RESTFul案例这篇文章有很多重复的地方,就当是复习吧。

表单提交发送PUT和DELETE请求

REST(Representational State Transfer,表述性状态转移)是一种架构风格,它使用HTTP请求方式动词表示对资源的操作。
在这里插入图片描述
具体看下面的例子吧。

  1. 新建Spring项目:demo3。
    新建时添加3个依赖:Lombok、Spring Configuration Processor和SpringWeb。
    项目新建好后,自动生成的pom.xml如下所示。
    另外,将自动生成的配置文件application.properties重命名为application.yml。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo3</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo3</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
  1. 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";
    }
}
  1. resources.static下新建欢迎页面index.html。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
  <form action="/user" method="get">
    <input type="submit" value="REST-GET"/>
  </form>
  <form action="/user" method="post">
    <input type="submit" value="REST-POST"/>
  </form>
  <form action="/user" method="put">
    <input type="submit" value="REST-PUT"/>
  </form>
  <form action="/user" method="delete">
    <input type="submit" value="REST-DELETE"/>
  </form>
</body>
</html>
  1. 启动应用,访问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<String> 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

<form method="post"></form>

2)发送给服务器的数据必须包括名为_method,值为putdelete的这么一个数据,可用input[type=“hidden”]实现。

<input type="hidden" name="_method" value="put"/>

按照以上思路修改index.html,修改后的结果如下,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
  <form action="/user" method="get">
    <input type="submit" value="REST-GET"/>
  </form>
  <form action="/user" method="post">
    <input type="submit" value="REST-POST"/>
  </form>
  <form action="/user" method="post">
    <input type="hidden" name="_method" value="put"/>
    <input type="submit" value="REST-PUT"/>
  </form>
  <form action="/user" method="post">
    <input type="submit" value="REST-DELETE"/>
  </form>
</body>
</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的这么一个数据,通常用<input type="hidden" name="_method" value="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,如下。

<form action="/user" method="post">
  <input type="hidden" name="_mymethod" value="put"/>
  <input type="submit" value="REST-PUT"/>
</form>
<form action="/user" method="post">
  <input type="hidden" name="_mymethod" value="delete"/>
  <input type="submit" value="REST-DELETE"/>
</form>

重启应用,访问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

最后,重启应用,发送请求。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值