API Architecture — Design Best Practices for REST APIs (Phần 1)

20 tháng 11, 2021 - 1822 lượt xem

REST APIs

1. Học kiến thức cơ bản về HTTP

Học kiến thức cơ bản về HTTP

Nếu bạn muốn xây dựng một REST API có thiết kế tốt, bạn phải biết những kiến thức cơ bản về giao thức HTTP, điều này sẽ giúp bạn có những lựa chọn thiết kế tốt

Tôi tìm thấy " Tổng quan về HTTP" trên tài liệu của Mozilla Developer Network. Đây là tài liệu tham khảo khá toàn diện cho chủ đề này

Liên quan đến việc thiết kế REST API thì đây là một TLDR của HTTP được áp dụng cho RESTful Design:

  • HTTP có các động từ (hành động hoặc phương thức): trong đó GET, POST, PUT, PATCHDELETE là phổ biến nhất.
  • REST được định hướng bởi tài nguyên và một tài nguyên được đại diện bởi một URI: /library/
  • Endpoint là sự kết hợp của một động từ và một URI, ví dụ: GET: /books/
  • Một endpoint có thể được hiểu là một hành động được thực hiện trên một tài nguyên. Ví dụ: POST: /books/ có thể có nghĩa là "Tạo một cuốn sách mới".
  • Ở cấp độ cao, các động từ ánh xạ đến các hoạt động CRUD như: GET nghĩa là Đọc, POST nghĩa là Tạo, PUTPATCH nghĩa là Cập nhậtDELETE nghĩa là Xóa
  • Trạng thái của response (response's status) được chỉ định bằng status code: 1xx cho thông tin, 2xx cho thành công, 3xx cho chuyển hướng, 4xx cho lỗi client và 5xx cho lỗi server

Tất nhiên bạn có thể sử dụng những thứ khác mà HTTP protocol cung cấp cho việc thiết kế API REST, nhưng đây là những điều cơ bản mà tôi tin rằng bạn phải ghi nhớ.

2. Không trả về plain text (văn bản thuần túy)

Không trả về plain text

Mặc dù điều này không bắt buộc bởi bất kỳ kiến trúc REST nào, nhưng theo quy ước, hầu hết các API REST sử dụng JSON làm định dạng dữ liệu trả về.

Tuy nhiên, sẽ không tốt nếu chỉ trả về một response body có chứa JSON-formatted String(chuỗi có định dạng JSON). Bạn vẫn nên chỉ định Content-Type cho header của response trả về. Nó phải là giá trị application/json.

Điều này đặc biệt quan trọng khi xử lý với application/programmatic clients (ví dụ: một service/API khác tương tác với API của bạn thông qua thư viện requests của Python) - một số trong số chúng dựa vào header để giải mã chính xác response.

3. Không sử dụng động từ trong URI

Không sử dụng động từ trong URI

Bây giờ nếu bạn đã hiểu những điều cơ bản, thì bạn sẽ nhận ra rằng sẽ not RESTful nếu đặt động từ trong URI. Nguyên nhân là do các HTTP method đã mô tả chính xác hành động đang được thực hiện trên tài nguyên.

Ví dụ: Giả sử bạn cung cấp một endpoint để tạo và truy xuất bìa sách cho một cuốn sách. Ý tưởng đầu tiên của bạn có thể là tạo một endpoint tương tự như sau:

GET: /books/:slug/generateBookCover/

Tuy nhiên, phương thức GET đã có ý nghĩa là truy xuất ("GETting") bìa sách. Vì vậy, hãy sử dụng:

GET: /books/:slug/bookCover/

Tương tự, đối với một endpoint "tạo một cuốn sách mới":

# Không nên
POST: /books/createNewBook/

# Nên
POST: /books/

4. Sử dụng danh từ số nhiều cho resources (tài nguyên)

Sử dụng danh từ số nhiều cho resources (tài nguyên)

Điều này có thể khó xác định, bạn nên sử dụng dạng số nhiều hay số ít cho tài nguyên.

Chúng ta nên sử dụng /book/:id/ (số ít) hay /books/:id/ (số nhiều)?

Lời khuyên của cá nhân tôi là sử dụng hình thức số nhiều.

Tại sao? Bởi vì nó phù hợp với tất cả các endpoints.

Tôi có thể thấy rằng GET /book/2/ là ổn. Nhưng GET /book/ thì sao? Chúng ta đang NHẬN cuốn sách duy nhất trong thư viện, một vài cuốn sách hay tất cả?

Để ngăn chặn những suy nghĩ mơ hồ này, hãy nhất quán sử dụng số nhiều ở mọi nơi:

GET: /books/2/
POST: /books/
...

5. Trả về chi tiết error trong response body

Trả về chi tiết error trong response body

Khi API server xử lý error, nó sẽ trả về chi tiết error trong JSON body để giúp người sử dụng phát hiện lỗi và debug. Thậm chí sẽ tốt hơn nếu response bao gồm những thông tin nào bị ảnh hưởng bởi error!

{
    "error": "Invalid payload.",
    "detail": {
        "name": "This field is required."
    }
}

6. Đặc biệt chú ý đến HTTP status codes

Đặc biệt chú ý đến HTTP status codes

Tôi cảm thấy điều này là khá quan trọng. Nếu có một điều bạn cần nhớ từ bài viết này, thì đây có lẽ là nó.

Điều tồi tệ nhất mà API của bạn có thể làm là trả về error response với status code là 200 OK.

Thay vào đó, hãy trả về HTTP status code có ý nghĩa và mô tả chính xác loại error.

Tuy nhiên, có lẽ bạn vẫn đang thắc mắc, “Nhưng tôi đang gửi thông tin chi tiết về error trong response body như bạn đã đề xuất, vậy điều đó có gì sai?”

Hãy để tôi kể cho bạn một câu chuyện

Tôi đã từng tích hợp một API trả về 200 OK cho mỗi response và cho biết liệu request có thành công hay không thông qua trường status:

{
    "status": "success",
    "data": {}
}

Mặc dù HTTP status code trả về 200 OK, nhưng tôi không thể hoàn toàn chắc chắn rằng server không xử lý sai request của tôi.

Trên thực tế, API có thể trả về các response như sau:

HTTP/1.1 200 OK
Content-Type: text/html
{
    "status": "failure",
    "data": {
        "error": "Expected at least three items in the list."
    }
}

Nó luôn trả về nội dung HTML. Với các kết quả như vậy, tôi luôn phải kiểm tra status code và trường "status" trong response trả về để chắc chắn rằng mọi thứ đều ổn trước khi tôi đọc thông tin từ response body

Kiểu thiết kế này thực sự không nên, bởi vì nó làm mất sự tin tưởng giữa API và người sử dụng. Bạn sợ rằng API có thể lừa dối bạn.

Tất cả những điều này là cực kỳ "un-RESTful". Thay vào đó bạn nên làm gì?

Sử dụng HTTP status code và chỉ sử dụng response body để cung cấp chi tiết lỗi.

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
    "error": "Expected at least three items in the list."
}

Nguồn tham khảo - tại đây

Bình luận

avatar
Trương Văn Tuấn 2021-11-21 09:18:37.216804 +0000 UTC

Cảm ơn tác giả, bài viết nhiều thông tin hay, áp dụng được ngay.

Avatar
* Vui lòng trước khi bình luận.
Ảnh đại diện
  +1 Thích
+1