HTTP PUT vs. POST in REST API

Người dịch: Nguyễn Thành Trung - Học viên lớp Java08
Email liên hệ: nguyenthanhtrung.nlk58@gmail.com
Bài viết gốc: https://www.baeldung.com/rest-http-put-vs-post

1. Tổng quát

Trong hướng dẫn này, chúng ta sẽ kiểm tra hai phương thức HTTP quan trọng, PUT và POST, mà chúng ta thường sử dụng trong REST architecture. Không có gì bí mật khi các nhà phát triển đôi khi phải đấu tranh để lựa chọn giữa hai phương pháp này trong khi thiết kế một RESTful web service. Do đó, chúng ta sẽ giải quyết vấn đề này bằng cách triển khai đơn giản ứng dụng RESTful trong Spring Boot.

2. Phân vân giữa PUT vs POST

Trong REST architecture điển hình , client gửi các yêu cầu dưới dạng phương thức HTTP đến server để tạo, truy xuất, sửa đổi hoặc loại bỏ tài nguyên. Mặc dù chúng ta có thể sử dụng cả PUT và POST để tạo tài nguyên, nhưng có sự khác biệt đáng kể giữa chúng về các ứng dụng dự định của chúng.

Theo tiêu chuẩn RFC 2616 , phương thức POST nên được sử dụng để yêu cầu server chấp nhận thực thể kèm theo làm cấp dưới của tài nguyên hiện có được xác định bởi Request-URI. Điều này có nghĩa là lời gọi phương thức POST sẽ tạo ra một tài nguyên con trong một tập hợp các tài nguyên.

Ngược lại, phương thức PUT nên được sử dụng để yêu cầu server lưu trữ thực thể kèm theo trong Request-URI được cung cấp. Nếu URI yêu cầu trỏ đến tài nguyên hiện có trên server, thực thể được cung cấp sẽ được coi là phiên bản sửa đổi của tài nguyên hiện có. Do đó, lệnh gọi phương thức PUT sẽ tạo một tài nguyên mới hoặc cập nhật một tài nguyên hiện có .

Một sự khác biệt quan trọng khác giữa các phương thức, PUT là một phương thức không đơn thuần, trong khi POST thì không . Ví dụ: gọi phương thức PUT nhiều lần sẽ tạo hoặc cập nhật cùng một tài nguyên. Ngược lại, nhiều yêu cầu POST sẽ dẫn đến việc tạo nhiều lần cùng một tài nguyên.

3. Ứng dụng mẫu

Để chứng minh sự khác biệt giữa PUT và POST, chúng ta sẽ tạo một ứng dụng web RESTful đơn giản bằng Spring Boot. Ứng dụng sẽ lưu trữ tên và địa chỉ của mọi người.

3.1. Maven Dependencies

Để bắt đầu, chúng ta cần các dependency cho Spring Web, Spring Data JPA và cơ sở dữ liệu H2 trong bộ nhớ trong tệp pom.xml:

<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. Domain Entity and Repository Interface

Hãy bắt đầu bằng cách tạo đối tượng domain trước. Đối với sổ địa chỉ, chúng ta sẽ xác định một lớp Entity được gọi là Address mà chúng ta sẽ sử dụng để lưu trữ thông tin địa chỉ của các cá nhân. Để đơn giản, chúng ta sẽ sử dụng ba trường: name, city và postalCode cho Address entity của chúng ta:

@Entity
public class Address {

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

    // constructors, getters, and setters
}

Bước tiếp theo là truy cập dữ liệu từ cơ sở dữ liệu. Để đơn giản, chúng ta sẽ tận dụng JpaRepository của Spring Data JPA. Điều này sẽ cho phép chúng ta thực hiện các chức năng CRUD trên dữ liệu mà không cần viết thêm bất kỳ mã nào:

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

3.3. REST Controller

Cuối cùng, chúng ta cần xác định API endpoints cho ứng dụng của mình. Chúng ta sẽ tạo một RestController sẽ sử dụng các yêu cầu HTTP từ client và gửi lại phản hồi thích hợp.

Ở đây, chúng ta sẽ xác định một @PostMapping để tạo địa chỉ mới và lưu trữ chúng trong cơ sở dữ liệu và @PutMapping để cập nhật nội dung của sổ địa chỉ dựa trên URI yêu cầu. Nếu không tìm thấy URI, nó sẽ tạo một địa chỉ mới và lưu trữ trong cơ sở dữ liệu:

@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);
            });
    }
    //additional methods omitted
}

3.4. cURL Requests

Bây giờ chúng ta có thể kiểm tra ứng dụng đã phát triển của mình bằng cách sử dụng cURL để gửi các yêu cầu HTTP mẫu đến server của chúng ta.

Để tạo một địa chỉ mới, chúng ta sẽ đính kèm dữ liệu ở định dạng JSON và gửi nó thông qua một yêu cầu POST:

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

Bây giờ chúng ta hãy cập nhật nội dung của địa chỉ mà chúng ta đã tạo. Chúng ta sẽ gửi một yêu cầu PUT bằng cách sử dụng id của địa chỉ đó trong URL. Trong ví dụ này, chúng ta sẽ cập nhật city và postalCode của address mà chúng ta vừa tạo. Chúng ta giả sử nó đã được lưu với id = 1:

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

4. Kết luận

Trong bài viết này, chúng ta đã thảo luận về sự khác biệt về khái niệm giữa các phương thức HTTP PUT và POST. Ngoài ra, chúng ta đã tìm hiểu cách thực hiện các phương pháp này bằng cách sử dụng Spring Boot framework để phát triển các ứng dụng RESTful.

Kết luận, chúng ta nên sử dụng phương thức POST để tạo một tài nguyên mới và phương thức PUT để cập nhật một tài nguyên hiện có.