7. Sử dụng HTTP status codes một cách nhất quán

Sử dụng HTTP status codes

Khi bạn đã thành thạo HTTP status codes, bạn nên cố gắng sử dụng chúng một cách nhất quán.

Ví dụ, nếu ở đâu đó bạn chọn status code là 201 Created cho một POST endpoint, thì bạn hãy sử dụng cùng một HTTP status code đó cho mọi POST endpoint.

Tại sao? Bởi vì người dùng không phải lo lắng về việc HTTP method sẽ trả về HTTP status code nào trong các trường hợp nào.

Vì vậy, hãy nhất quán trong việc sử dụng HTTP status code. Thông thường, tôi tuân theo quy tắc sau:

GET: 200 OK
PUT: 200 OK
POST: 201 Created
PATCH: 200 OK
DELETE: 204 No Content

8. Không lồng ghép resources

Không lồng ghép resources

Bạn có thể nhận thấy rằng các API REST xử lý resources. Việc truy xuất một danh sách resource hoặc resource đơn lẻ rất đơn giản. Nhưng, điều gì sẽ xảy ra khi bạn xử lý các resources có liên quan đến nhau?

Ví dụ: giả sử chúng ta muốn truy xuất tất cả sách của một tác giả cụ thể - tác giả có name=Cagan. Về cơ bản có hai lựa chọn.

Lựa chọn đầu tiên sẽ là lồng resource books dưới resource authors, ví dụ:

GET: /authors/Cagan/books/

Một số người recommend sử dụng quy ước này vì nó đại diện cho mối quan hệ một-nhiều giữa một tác giả và sách của họ.

Tuy nhiên, nó không rõ ràng về loại resource mà bạn đang yêu cầu. Nó là tác giả hay nó là sách?

Đề xuất của cá nhân tôi là sử dụng query string parameters để lọc trực tiếp resource books:

GET: /books?authorName=Cagan

Và điều này có nghĩa là: “Lấy tất cả sách của tác giả có tên là Cagan”, phải không? 🙂

9. Sử dụng chính xác dấu "/"

Sử dụng chính xác dấu "/"

Các URI nên có dấu gạch chéo "/" ở cuối hay không thực sự không phải là vấn đề. Bạn có thể chọn có hoặc không có dấu gạch chéo ở cuối và áp dụng vào các API của mình.

(Tôi sẽ thừa nhận, bản thân tôi đã phạm lỗi này hơn một lần. 🙈)

Một ngày nọ, khi tôi đang tích hợp API REST vào một trong các dự án của mình và tôi liên tục nhận được HTTP 500 Internal Error cho mỗi lần request. Endpoint tôi sử dụng như sau:

POST: /buckets

Tôi rất bực mình và tôi không hiểu mình đã làm sai cái quái gì. 🤪

Cuối cùng, hóa ra là server bị lỗi vì tôi đã bỏ sót một dấu gạch chéo "/"! Vì vậy, tôi đã bắt đầu sử dụng:

POST: /buckets/

Và mọi thứ tốt đẹp sau đó. Hy vọng bạn có thể khắc phục vấn đề này cho người dùng của mình.

10. Sử dụng querystring để filtering và pagination

Sử dụng querystring để lọc và phân trang

Trong phần lớn trường hợp, một endpoint không đủ để đáp ứng các yêu cầu phức tạp khác nhau.

Người dùng của bạn có thể muốn truy xuất các item đáp ứng một điều kiện cụ thể hoặc lấy chúng với số lượng nhỏ tại một thời điểm để cải thiện hiệu suất.

Đó chính là lý do filteringpagination được tạo ra.

Với filtering, người dùng có thể chỉ định các tham số (hoặc thuộc tính) mà các item trả về phải có.

Pagination cho phép người dùng truy xuất các phần nhỏ của tập dữ liệu. Loại phân trang đơn giản nhất là phân theo số trang, điều này được xác định bởi pagepage_size.

Bây giờ, câu hỏi đặt ra là: Làm cách nào để bạn tích hợp các tính năng như vậy vào API REST?

Câu trả lời của tôi là: Sử dụng querystring.

Tôi sẽ nói tại sao bạn nên sử dụng querystring để phân trang. Nó sẽ trông như sau:

GET: /books?page=1&page_size=10

Trường hợp muốn lấy các cuốn sách đã được xuất bản thì sao? Lúc đầu, bạn có thể nghĩ đến cần làm như thế này để lấy tất cả các sách đã xuất bản:

GET: /books/published/

Vấn đề trong thiết kế: Các cuốn sách đã xuất bản không phải là resource! Thay vào đó, nó là một đặc điểm của dữ liệu bạn đang truy xuất. Nó nên được xuất hiện ở trong querystring.

Cuối cùng, người dùng có thể lấy “trang thứ hai của sách đã xuất bản” như sau:

GET: /books?published=true&page=2&page_size=10

Đẹp hơn rồi, phải không?

11. Biết sự khác nhau giữa 401 Unauthorized và 403 Forbidden

sự khác nhau giữa 401 Unauthorized và 403 Forbidden

Khi xử lý các lỗi bảo mật trong API REST, rất dễ nhầm lẫn về việc lỗi đó có liên quan đến Authentication hay Authorization

Đây là gợi ý của tôi, tùy thuộc vào tình huống để xử lý:

  • Người dùng không cung cấp thông tin xác thực? Ví dụ JWT Token của họ không hợp lệ hay hết thời gian sử dụng không? 👉 401 Unauthorized.
  • Người dùng đã được xác thực nhưng họ không có quyền để truy cập resource? 👉 403 Forbidden.

12. Sử dụng HTTP 202 Accepted

Tôi thấy 202 Accepted là một sự thay thế rất tiện dụng cho 201 Created. Về cơ bản nó có nghĩa là:

Tôi là server, tôi đã hiểu yêu cầu của bạn. Tôi chưa tạo resource, nhưng điều đó ổn.

Có hai trường hợp mà tôi thấy 202 Accepted đặc biệt phù hợp bao gồm:

  • Nếu resource sẽ được tạo ra do quá trình xử lý nào đó trong tương lai - ví dụ: Sau khi một công việc/quy trình kết thúc.
  • Nếu resource đã tồn tại theo một cách nào đó, và điều này không nên được coi là error.

13. Sử dụng web framework chuyên về REST APIs

Sử dụng web framework chuyên về REST APIs

Đây là best practice cuối cùng, chúng ta cùng thảo luận về câu hỏi này: Làm cách nào để bạn thực sự có thể triển khai các best practices trong API của mình?

Trong hầu hết trường hợp, bạn muốn tạo một API nhanh để một số service có thể tương tác với nhau.

Các lập trình Python sẽ chọn Flask, các lập trình JavaScript sẽ chọn Node (Express) và họ sẽ triển khai một số route đơn giản để xử lý các HTTP request.

Ví dụ: cả Flask và Express đều là hai framework rất linh hoạt, nhưng chúng không được tạo riêng để giúp bạn xây dựng các API REST.

Do đó, bạn phải thực hiện thêm các bước để triển khai các best practices trong API của mình. Và trong hầu hết các trường hợp, nếu bạn lười biếng, hoặc không nỗ lực sẽ dẫn đến trường hợp tạo ra API kỳ quoặc.

Giải pháp rất đơn giản: Sử dụng đúng tool cho công việc.

Các frameworks mới đã xuất hiện của nhiều ngôn ngữ khác nhau được tạo ra để xây dựng các API REST. Chúng giúp bạn thực hiện best practices một cách dễ dàng mà không làm giảm năng suất.

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