RESTful API 中的 PUT 和 POST 请求

1、概览

在设计 RESTful API 的时候,往往会纠结于到底是用 PUT 还是 POST 请求?

本文将会带你了解在 RESTful API 中 PUT 和 POST 请求之间的区别以及它们的应用场景。

2、PUT 与 POST

在典型的 REST 架构中,客户端以 HTTP 方法的形式向服务器发送请求,以创建、检索、修改或删除资源。虽然可以使用 PUTPOST 来创建资源,但它们在预期应用方面有很大的不同。

根据 RFC 2616 标准,POST 方法应该用于请求服务器将所附实体作为现有资源(由请求 URI 标识)的子级接受。这意味着使用 POST 方法调用将在资源集合下创建一个子资源。

相反,PUT 方法应该用于请求服务器将所附实体存储在请求的 URI 下。如果请求 URI 指向服务器上的现有资源,则提供的实体将被视为现有资源的修改版本。因此,PUT 方法调用要么创建一个新资源,要么更新现有资源。

这两种方法的另一个重要区别是,PUT 是一种幂等方法,而 POST 不是。例如,多次调用 PUT 方法将创建或更新同一资源。相比之下,多次 POST 请求将导致多次创建同一资源。

3、示例应用

使用 Spring Boot 创建一个简单的 RESTful Web 应用来演示 PUT 和 POST 请求的区别。

3.1、Maven 依赖

需要在 pom.xml 中添加 Spring Web、Spring Data JPA 和 H2 内存数据库依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

3.2、实体和 Repository 接口

创建 Address 实体类,包含三个字段 namecitypostalCode

@Entity
public class Address {

    private @Id @GeneratedValue Long id;
    private String name;
    private String city;
    private String postalCode;

    // 构造函数、get、set 方法省略
}

创建 AddressRepository Repository,继承自 JpaRepository 接口:

public interface AddressRepository extends JpaRepository<Address, Long> {
}

3.3、REST Controller

最后,创建 RestControlle。定义一个 @PostMapping 端点用于创建新的 Address 并将其存储到数据库中,以及一个 @PutMapping 端点用于根据请求 URI 更新 Address。如果未找到 URI,则创建一个新的 Address 并将其存储到数据库中:

@RestController
public class AddressController {

    private final AddressRepository repository;

    AddressController(AddressRepository repository) {
        this.repository = repository;
    }

    @PostMapping("/addresses")
    Address createNewAddress(@RequestBody Address newAddress) {
        return repository.save(newAddress);
    }

    @PutMapping("/addresses/{id}")
    Address replaceEmployee(@RequestBody Address newAddress, @PathVariable Long id) {

        return repository.findById(id)
            .map(address -> {
                address.setCity(newAddress.getCity());
                address.setPin(newAddress.getPostalCode());
                return repository.save(address);
            })
            .orElseGet(() -> {
                return repository.save(newAddress);
            });
    }
}

3.4、cURL 请求

现在,使用 cURL 向服务器发送 HTTP 请求来测试我们开发的应用。

使用 POST 请求创建新的 Address

curl -X POST --header 'Content-Type: application/json' \
    -d '{ "name": "John Doe", "city": "Berlin", "postalCode": "10585" }' \ 
    http://localhost:8080/addresses

接着,更新已创建的 Address,在 URL 中使用其 id 发送 PUT 请求。

在本例中,更新刚刚创建的 Address 中的 citypostalCode 部分。假设它的 id1

curl -X PUT --header 'Content-Type: application/json' \
  -d '{ "name": "John Doe", "city": "Frankfurt", "postalCode": "60306" }' \ 
  http://localhost:8080/addresses/1

4、总结

本文介绍了 HTTP PUT 和 POST 方法在概念上的区别,以及如何使用 Spring Boot 来实现这些方法,用于开发 RESTful 应用。

总结来说,POST 请求用于创建资源,多次调用会创建相同的资源。PUT 请求用于创建或者更新资源,如果资源不存在就创建,如果资源存着就更新,所以 PUT 请求是幂等的。


Ref:https://www.baeldung.com/rest-http-put-vs-post