Giới thiệu về Kiến trúc REST

Chào mừng bạn đến với các khái niệm nền tảng của các dịch vụ web hiện đại. Representational State Transfer, được biết đến rộng rãi với từ REST, là một phong cách kiến trúc xác định một tập ràng buộc dùng để tạo dịch vụ web. Được phát triển bởi Roy Fielding trong luận văn tiến sĩ, REST không phải là một tiêu chuẩn hay giao thức, mà là một triết lý thiết kế ở mức cao. Nó cung cấp một cách chuẩn hóa để các hệ thống máy tính giao tiếp trên Internet, đảm bảo các ứng dụng khác nhau có thể hiểu nhau một cách thông suốt. Bằng cách tuân thủ các nguyên tắc REST, các nhà phát triển có thể xây dựng các hệ thống có khả năng mở rộng, chịu lỗi và dễ bảo trì theo thời gian.

Vị trí cốt lõi của phong cách kiến trúc này là nguyên tắc mọi thứ đều là tài nguyên. Một tài nguyên có thể là bất kỳ mẩu thông tin hoặc thực thể dữ liệu nào mà một khách hàng có thể muốn tương tác, như hồ sơ khách hàng, bài đăng trên blog, hoặc một giao dịch tài chính. REST qui định rằng hệ thống nên giao tiếp Stateless, dựa vào hạ tầng web solid và có sẵn. Điều này có nghĩa là tận dụng các giao thức internet chuẩn, hầu như chỉ là Hypertext Transfer Protocol, để truyền dữ liệu giữa máy khách yêu cầu thông tin và máy chủ lưu trữ nó.

Hiểu REST đòi hỏi thay đổi tư duy từ suy nghĩ về hành động sang suy nghĩ về các đối tượng. Trong các kiến trúc gọi thủ tục từ xa (RPC) cũ, các hệ thống giao tiếp bằng cách nói cho nhau biết những chức năng cần thực thi. Trong kiến trúc RESTful, các hệ thống giao tiếp bằng cách trao đổi biểu diễn của trạng thái hiện tại của một tài nguyên. Khi một máy khách yêu cầu một tài nguyên, máy chủ trả về một payload đại diện cho tài nguyên tại thời điểm cụ thể đó, được định dạng bằng một phương tiện được hiểu toàn cầu như JavaScript Object Notation hoặc Extensible Markup Language.

Khái niệm về Tài nguyên và URIs

Vì REST quay quanh tài nguyên, việc nhận diện các tài nguyên này một cách chính xác và nhất quán là rất quan trọng. Trong kiến trúc REST, mỗi tài nguyên được gán một Uniform Resource Identifier, đóng vai trò như địa chỉ duy nhất trên web. Định danh này phải mang tính logic, có cấu trúc phân cấp và dễ hiểu đối với nhà phát triển. Một cấu trúc địa chỉ được thiết kế tốt cho phép người tiêu dùng của API dự đoán nơi có thể tìm thấy dữ liệu liên quan mà không cần phải nhớ tài liệu phức tạp.

Thực hành chuẩn ngành cho REST là sử dụng danh từ ở dạng số nhiều để đặt tên cho các tài nguyên này. Thay vì dùng động từ trong địa chỉ để mô tả một hành động, địa chỉ chỉ mô tả thực thể. Ví dụ, nếu bạn đang xây dựng giao diện để quản lý hồ sơ nhân viên, địa chỉ cơ sở của bạn nên chỉ là /employees. Phương pháp dựa trên danh từ này giúp địa chỉ gọn gàng và tập trung hoàn toàn vào chủ thể của dữ liệu thay vì thao tác được thực hiện. Sử dụng các động từ như /get-employees hoặc /create-employee là vi phạm cơ bản các nguyên tắc thiết kế REST.

Khi một khách hàng cần tương tác với một tài nguyên đơn lẻ, thay vì một tập hợp tài nguyên, các định danh duy nhất được chèn vào đường dẫn địa chỉ. Nếu khách hàng muốn truy cập hồ sơ của một nhân viên có số nhận dạng trong cơ sở dữ liệu là 42, địa chỉ thích hợp sẽ trở thành /employees/42. Cấu trúc phân cấp này có thể được mở rộng để thể hiện các mối quan hệ giữa các loại tài nguyên khác nhau. Nếu bạn cần xem bảng lương cho nhân viên cụ thể đó, địa chỉ tự động mở rộng thành /employees/42/payrolls.

Các Phương thức HTTP chuẩn trong REST

Mã nhận dạng nguồn (Uniform Resource Identifier) cung cấp địa chỉ của dữ liệu, còn phương thức HTTP cung cấp hành động. REST tận dụng các phương thức chuẩn được tích hợp trong Hypertext Transfer Protocol để chỉ dẫn máy chủ về thao tác nào cần thực hiện đối với tài nguyên được nhắm tới. Sự phân tách giữa địa chỉ và hành động này chính là điều làm cho các giao diện RESTful trở nên có Predicable. Bốn phương thức phổ biến nhất bạn sẽ sử dụng hàng ngày là GET, POST, PUT, và DELETE.

Phương thức GET được thiết kế dành riêng để truy vấn thông tin. Khi một máy khách gửi yêu cầu GET, nó đang yêu cầu máy chủ trả về một đại diện của tài nguyên mà không làm biến đổi nó theo bất kỳ cách nào. Phương thức này được định nghĩa là vừa an toàn vừa bất biến (idempotent). Một phương thức an toàn có nghĩa là nó không làm thay đổi trạng thái của máy chủ, và một phương thức bất biến có nghĩa là thực hiện đúng cùng một yêu cầu mười lần sẽ cho ra kết quả hoàn toàn giống như thực hiện một lần. Bạn có thể xem một yêu cầu GET như việc đọc một tài liệu.

Phương thức POST được sử dụng để tạo ra các tài nguyên hoàn toàn mới trên máy chủ. Khi một máy khách gửi một yêu cầu POST tới địa chỉ tập hợp, nó sẽ bao gồm dữ liệu đầu vào chứa dữ liệu cho thực thể mới. Khác với phương thức GET, POST vừa không an toàn vừa không bất biến. Gửi đi cùng một yêu cầu POST nhiều lần sẽ tạo ra nhiều tài nguyên khác nhau trên máy chủ. Máy chủ xử lý payload, tạo ra một định danh duy nhất cho tài nguyên mới và lưu trữ nó vào cơ sở dữ liệu.

Để sửa đổi các tài nguyên hiện có, các nhà phát triển dựa vào các phương thức PUT và DELETE. Phương thức PUT cập nhật một tài nguyên bằng cách hoàn toàn thay thế trạng thái hiện tại bằng trạng thái mới được cung cấp trong payload của yêu cầu. Nếu tài nguyên không tồn tại, một yêu cầu PUT có thể tạo ra nó, dù thường được dùng cho cập nhật toàn bộ. Phương thức DELETE, như chính tên gọi của nó, yêu cầu máy chủ xóa vĩnh viễn tài nguyên được chỉ định. Cả PUT và DELETE đều được định nghĩa là bất biến (idempotent), có nghĩa là gửi nhiều yêu cầu giống nhau để cập nhật hoặc xóa cùng một tài nguyên cụ thể sẽ để lại hệ thống ở cùng trạng thái như lần yêu cầu đầu tiên.

Đối chiếu các thao tác với logic kinh doanh

Trong kỹ thuật phần mềm, các thao tác nền tảng để lưu trữ bền vững gồm Create, Read, Update và Delete. REST cung cấp một sự ánh xạ trực tiếp giữa các thao tác cơ sở dữ liệu này và các phương thức HTTP mà chúng ta vừa thảo luận. Create tương thích hoàn hảo với POST, Read ánh xạ đúng với GET, Update kết nối với PUT, và Delete tương ứng với phương thức DELETE. Sự ánh xạ chuẩn hóa này có nghĩa là khi một nhà phát triển hiểu mô hình dữ liệu cơ bản, họ sẽ intuitively biết cách tương tác với giao diện.

Hãy xem một tình huống kinh doanh thực tế khi bạn đang xây dựng hệ thống quản lý tồn kho cho một hiệu sách bán lẻ. Tài nguyên cốt lõi trong hệ thống này là một cuốn sách. Khi một nhà xuất bản phát hành một cuốn tiểu thuyết mới, người quản lý tồn kho cần thêm nó vào hệ thống. Ứng dụng khách sẽ biên tập chi tiết sách, như tiêu đề, tác giả và giá, và gửi chúng bên trong một yêu cầu POST tới địa chỉ /books. Máy chủ nhận được điều này, tạo bản ghi và thường trả về số nhận diện mới được gán.

Sau này, một nhân viên nhận thấy rằng giá của một cuốn sách cụ thể được nhập sai. Để sửa, ứng dụng khách gửi một yêu cầu PUT tới địa chỉ tài nguyên cụ thể, chẳng hạn /books/892. Payload của yêu cầu này chứa đầy đủ chi tiết sách đã được sửa lại. Máy chủ tìm cuốn sách có định danh đó và thay thế dữ liệu hiện tại bằng dữ liệu đã được sửa. Nếu cuốn sách bị ngừng phát hành và cần bị loại khỏi danh mục hoạt động hoàn toàn, ứng dụng khách gửi một yêu cầu DELETE tới địa chỉ cụ thể đó.

Một biểu đồ ánh xạ trực quan hiển thị bốn thao tác CRUD ở bên trái, được kết nối bằng các mũi tên tới các phương thức HTTP tương ứng GET POST PUT và DELETE ở bên phải, với các URI ví dụ cho một hệ thống tồn kho cửa hàng sách

Tính trạng không lưu trạng thái (statelessness) và phản hồi của máy chủ

Một trong những ràng buộc quan trọng nhất của REST là mọi tương tác giữa khách hàng và máy chủ phải hoàn toàn không lưu trạng thái. Điều này có nghĩa là máy chủ không được phép lưu trữ bất kỳ thông tin nào về phiên làm việc của khách hàng giữa các yêu cầu riêng lẻ. Mỗi yêu cầu được initiated bởi khách hàng phải chứa toàn bộ ngữ cảnh, thông tin xác thực và dữ liệu cần thiết để máy chủ hiểu và thực hiện thao tác. Máy chủ xem mỗi yêu cầu đến như một giao dịch độc lập hoàn toàn, không biết về bất kỳ yêu cầu nào đã đến trước đó.

Ràng buộc này về trạng thái không lưu trữ nghiêm ngặt là điều làm cho các kiến trúc REST có khả năng mở rộng cực lớn. Vì máy chủ không cần cấp phát bộ nhớ để theo dõi các phiên người dùng, nó giải phóng tài nguyên tính toán đáng kể. Hơn nữa, trong các hệ thống phân tán lớn đằng sau một bộ cân bằng tải, bất kỳ máy chủ nào trong một nhóm khổng lồ có thể xử lý bất kỳ yêu cầu nào từ bất kỳ khách hàng nào. Nếu một máy chủ gặp sự cố, máy chủ khác có thể ngay lập tức đảm nhận vai trò thay thế mà người dùng không bị mất dữ liệu phiên của họ, vì dữ liệu phiên chỉ tồn tại ở phía client.

Để truyền tải kết quả của các giao dịch độc lập này, máy chủ sử dụng các mã trạng thái HTTP tiêu chuẩn. Những con số ba chữ số này ngay lập tức thông báo cho client liệu thao tác có thành công hay thất bại. Các mã ở phạm vi hai trăm cho biết thành công, như 200 OK hoặc 201 Created. Các mã ở phạm vi bốn trăm ám chỉ lỗi phía client, nghĩa là yêu cầu bị định hình sai hoặc client yêu cầu một tài nguyên không tồn tại. Cuối cùng, các mã ở phạm vi năm trăm cho biết máy chủ gặp lỗi bất ngờ khi cố gắng xử lý một yêu cầu của client hoàn toàn hợp lệ.

Lỗi phổ biến trong thiết kế REST

Mặc dù REST được áp dụng rộng rãi, các nhà phát triển thường mắc các sai lầm thiết kế vi phạm nguyên tắc cốt lõi của nó. Sai lầm phổ biến nhất là trở lại thói quen gọi hàm từ xa bằng cách nhúng động từ vào địa chỉ tài nguyên. Các địa chỉ như /add-book hay /update-user làm suy yếu sự đồng nhất của giao diện. Phương thức HTTP đã mô tả hành động, vì vậy việc thêm một động từ vào địa chỉ sẽ tạo ra sự trùng lặp và nhầm lẫn. Địa chỉ nên xác định danh từ một cách nghiêm ngặt, trong khi phương thức HTTP xử lý động từ.

Một sai lầm nghiêm trọng khác là lạm dụng phương thức GET để sửa đổi hoặc xóa dữ liệu. Đôi khi các nhà phát triển tạo các địa chỉ như /users/5/delete và hướng dẫn khách hàng truy cập nó bằng một yêu cầu GET. Điều này cực kỳ nguy hiểm vì hạ tầng web chuẩn xem các yêu cầu GET là các thao tác an toàn. Trình duyệt web tải trước chúng, máy chủ lưu cache sao chép chúng, và trình thu thập quả cầu của công cụ tìm kiếm sẽ lập chỉ mục chúng tự động. Nếu một yêu cầu GET kích hoạt việc xóa, một trình thu thập web đơn giản xuyên qua trang web của bạn có thể vô tình xóa toàn bộ cơ sở dữ liệu của bạn.

Cuối cùng, sự không nhất quán về dạng số nhiều tạo ra sự cản trở cho nhà phát triển sử dụng giao diện. Nếu địa chỉ để lấy tất cả người dùng ở dạng số nhiều là /users, nhưng địa chỉ để lấy một tài khoản ở dạng số ít là /account, người tiêu dùng phải liên tục tham khảo tài liệu để nhớ cách viết. Chuẩn công nghiệp được chấp nhận là chỉ sử dụng danh từ ở dạng số nhiều cho tất cả bộ sưu tập và tài nguyên cá nhân. Sự nhất quán là đặc điểm nổi bật của một giao diện REST được thiết kế tốt, đảm bảo nó trực quan và dễ tích hợp với thời gian dài sau này.

Bài kiểm tra cuối bài

1. REST viết tắt của từ gì trong ngữ cảnh của dịch vụ web?

2. Trong số các lựa chọn sau, đâu là thực hành chuẩn của ngành về đặt tên tài nguyên trong một REST URI?

3. Phương thức HTTP nào được thiết kế và chỉ dành cho việc lấy thông tin và được coi là an toàn cũng như idempotent?

4. Cách một ứng dụng khách nên yêu cầu máy chủ để tạo ra một tài nguyên hoàn toàn mới?

5. Tại sao ràng buộc Statelessness lại quan trọng đối với kiến trúc máy chủ trong REST?

6. Điều gì là hậu quả nguy hiểm của việc sử dụng phương thức GET để xóa một tài nguyên?

Giới thiệu về Nguyên tắc REST và các Phương thức HTTP