API RESTful: Động từ HTTP có URL được chia sẻ hoặc cụ thể?


25

Trong khi tạo API RESTful , tôi có nên sử dụng Động từ HTTP trên cùng một URL (khi có thể) hay tôi nên tạo một URL cụ thể cho mỗi hành động?

Ví dụ:

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

Hoặc với các URL cụ thể như:

GET     /items            # Read all items
GET     /item/:id         # Read one item
POST    /items/new        # Create a new item
PUT     /item/edit/:id    # Update one item
DELETE  /item/delete/:id  # Delete one item

Câu trả lời:


46

Trong sơ đồ sau của bạn, bạn giữ các động từ trong các URL của tài nguyên của bạn. Điều này nên tránh vì các động từ HTTP nên được sử dụng cho mục đích đó. Ôm giao thức cơ bản thay vì bỏ qua, sao chép hoặc ghi đè lên giao thức.

Chỉ cần nhìn vào DELETE /item/delete/:id, bạn đặt cùng một thông tin hai lần trong cùng một yêu cầu. Điều này là thừa và nên tránh. Cá nhân, tôi sẽ bối rối với điều này. API có thực sự hỗ trợ DELETEcác yêu cầu không? Nếu tôi đặt deleteURL và sử dụng động từ HTTP khác thì sao? Nó sẽ phù hợp với bất cứ điều gì? Nếu vậy, cái nào sẽ được chọn? Là khách hàng của API được thiết kế phù hợp, tôi không cần phải hỏi những câu hỏi như vậy.

Có lẽ bạn cần nó để bằng cách nào đó hỗ trợ khách hàng không thể phát hành DELETEhoặc PUTyêu cầu. Nếu đó là trường hợp, tôi sẽ chuyển thông tin này trong tiêu đề HTTP. Một số API sử dụng một X-HTTP-Method-Overridetiêu đề cho mục đích cụ thể này (dù sao tôi nghĩ nó khá xấu xí). Tôi chắc chắn sẽ không đặt các động từ trong các đường dẫn mặc dù.

Đi

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

Điều quan trọng về các động từ là chúng đã được xác định rõ trong đặc tả HTTP và tuân thủ các quy tắc này cho phép bạn sử dụng bộ nhớ cache, proxy và có thể các công cụ khác bên ngoài ứng dụng của bạn để hiểu ngữ nghĩa của HTTP nhưng không phải là ngữ nghĩa ứng dụng của bạn . Xin lưu ý rằng lý do bạn nên tránh có chúng trong các URL của mình không phải là về các API RESTful yêu cầu các URL có thể đọc được. Đó là về việc tránh sự mơ hồ không cần thiết.

Hơn nữa, API RESTful có thể ánh xạ các động từ này (hoặc bất kỳ tập hợp con nào) sang bất kỳ tập hợp ngữ nghĩa ứng dụng nào, miễn là nó không đi ngược lại đặc tả HTTP. Ví dụ, nó là hoàn toàn có thể xây dựng một API RESTful mà chỉ sử dụng GET yêu cầu nếu tất cả các hoạt động mà nó cho phép là cả hai an toànidempotent . Ánh xạ trên chỉ là một ví dụ phù hợp với trường hợp sử dụng của bạn và tuân thủ thông số kỹ thuật. Nó không nhất thiết phải như thế này.

Cũng xin lưu ý rằng API RESTful thực sự không bao giờ yêu cầu lập trình viên đọc tài liệu mở rộng về các URL có sẵn miễn là bạn tuân thủ nguyên tắc HATEOAS (Siêu văn bản như Trạng thái Công cụ), đây là một trong những giả định cốt lõi của REST . Các liên kết có thể hoàn toàn không thể hiểu được đối với con người miễn là ứng dụng khách có thể hiểu và sử dụng chúng để tìm ra các chuyển đổi trạng thái ứng dụng có thể.


4
Trong trường hợp không có PUTDELETE, tôi sẽ ưu tiên thêm nó vào đường dẫn, không phân biệt nó với một chuỗi truy vấn. Đây không phải là một sửa đổi chuỗi truy vấn cho một hoạt động hiện có; đó là một hoạt động riêng biệt.
Robert Harvey

4
@RobertHarvey trong trường hợp này, dù sao tôi cũng gọi đó là hack. Như bạn nói, đó là một hoạt động và đó không phải là điều tôi đặt ra khi thiết kế API nhằm mục đích trở thành RESTful. Đặt nó trong chuỗi truy vấn có vẻ ít xâm lấn hơn. Nó không ngăn chặn bộ nhớ đệm nhưng tôi không nghĩ rằng phản hồi cho loại yêu cầu này nên được lưu vào bộ đệm. Nó cũng cho phép người tiêu dùng API dễ dàng chỉ ra phương thức mà không cần phân tích cú pháp hoặc xây dựng URL. Lý tưởng nhất, một API RESTful thực sự sẽ cung cấp các siêu liên kết mà không yêu cầu khách hàng tự xây dựng URL.
toniedzwiedz

Nếu bạn không có tất cả các động từ, dù sao nó cũng không hoàn toàn RESTful, phải không?
Robert Harvey

@RobertHarvey đúng nhưng tôi coi những điều này như một dự phòng, không phải là thiết kế dự định. Tôi tưởng tượng API nên hỗ trợ các phương thức HTTP thực tế và nếu một số khách hàng không thể triển khai chúng vì bất kỳ lý do gì, họ chỉ có thể thay thế việc sử dụng của họ bằng các tham số truy vấn này. Một proxy thậm chí có thể lấy những thứ này một cách nhanh chóng và chuyển đổi các yêu cầu thành các động từ sử dụng các động từ HTTP chính hãng để máy chủ thậm chí không cần phải quan tâm. Rất ít API xung quanh thực sự RESTful. Khi nói đến các API web chung, đó thực sự là một vấn đề về hương vị. Cá nhân, tôi sẽ tìm các URL sạch. Dễ hiểu hơn IMHO.
toniedzwiedz

1
@RobertHarvey như đã giải thích, hầu như không phải là cách dự định sử dụng chúng. Tôi chỉ thấy điều này ít hơn trong hai tệ nạn khi bạn phải vượt qua giới hạn của khách hàng. Tôi nhớ lại việc đọc tài liệu cho một API như vậy nhưng tôi sẽ phải thực hiện một số khai quật trong lịch sử / dấu trang trình duyệt của mình để tìm thấy nó. Bây giờ tôi nghĩ về nó, một tiêu đề có thể tốt hơn trong trường hợp này. Bạn có đồng ý không
toniedzwiedz

14

Cái đầu tiên.

URI / URL là một định danh tài nguyên (gợi ý trong tên: định danh tài nguyên thống nhất). Với quy ước đầu tiên, tài nguyên được nói đến khi bạn thực hiện "NHẬN / người dùng / 123" và tài nguyên được nói đến khi bạn thực hiện "XÓA / người dùng / 123" rõ ràng là cùng một tài nguyên vì chúng có cùng một URL.

Với quy ước thứ hai, bạn không thể chắc chắn rằng "GET / user / 123" và "DELETE / user / xóa / 123" thực sự là cùng một tài nguyên và dường như ngụ ý rằng bạn đang xóa một tài nguyên liên quan thay vì tài nguyên chính nó, vì vậy sẽ khá ngạc nhiên khi xóa /user/delete/123thực sự xóa /user/123. Nếu bạn có mọi hoạt động trên các URL khác nhau, URI không còn hoạt động như một định danh tài nguyên.

Khi bạn nói DELETE /user/123, bạn đang nói "xóa 'hồ sơ người dùng với id 123'". Trong khi nếu bạn nói DELETE /user/delete/123, điều bạn dường như ngụ ý là "xóa 'bản ghi xóa người dùng bằng id 123'", đó có lẽ không phải là điều bạn muốn nói. Và ngay cả khi bạn sử dụng động từ đúng hơn trong tình huống này: "POST / user / xóa / 123" có nghĩa là "thực hiện thao tác gắn với 'xóa người dùng với id 123'", đây vẫn là một cách làm tròn để nói xóa một bản ghi (đây là giống như danh từ của một động từ trong tiếng Anh).

Một cách bạn có thể nghĩ về URL là coi nó như con trỏ tới các đối tượng và tài nguyên như các đối tượng trong lập trình hướng đối tượng. Khi bạn làm như vậy GET /user/123, DELETE /user/123bạn có thể nghĩ về chúng như các phương thức trong đối tượng : [/user/123].get(), [/user/123].delete()trong đó []giống như một toán tử hội nghị con trỏ nhưng cho các URL (nếu bạn biết một ngôn ngữ có con trỏ). Một trong những nguyên tắc cơ bản của REST là giao diện thống nhất, nghĩa là có một bộ động từ / phương thức nhỏ và giới hạn, hoạt động cho mọi thứ trong một mạng lưới tài nguyên / đối tượng rộng lớn.

Do đó, cái đầu tiên là tốt hơn.

PS: tất nhiên, điều này đang nhìn vào REST theo cách thuần túy nhất. Đôi khi tính thực tế đánh bại sự thuần khiết, và bạn cần phải nhượng bộ cho những khách hàng chết não hoặc khuôn khổ khiến cho việc thực hiện REST đúng cách trở nên khó khăn.


+1 cho ví dụ OOP :)
53777A

6

(xin lỗi, lần đầu tiên tôi đã bỏ qua / chỉnh sửa / và / xóa / trong (2) ...)

Ý tưởng của URI là nó là một định danh của một tài nguyên có thể định địa chỉ , chứ không phải là một lời gọi phương thức . Vì vậy, URI nên trỏ đến một tài nguyên cụ thể. Và nếu bạn trì hoãn URI, bạn sẽ luôn nhận được cùng một tài nguyên.

Đó là, bạn nên nghĩ về các URI giống như cách bạn nghĩ về Khóa chính của một hàng trong cơ sở dữ liệu. Nó xác định duy nhất một cái gì đó: Mã định danh tài nguyên chung.

Vì vậy, cho dù bạn sử dụng số nhiều hay số ít, URI nên là một định danh chứ không phải là một lời gọi . Những gì bạn đang cố gắng thực hiện trong phương thức, cụ thể là: GET (get), PUT (tạo / cập nhật), DELETE (xóa) hoặc POST (mọi thứ khác).

Vì vậy, "/ item / xóa / 123" phá vỡ REST vì nó không trỏ đến tài nguyên, nó giống như một lời gọi phương thức.

(Ngoài ra, về mặt ngữ nghĩa, bạn sẽ có thể NHẬN một URI, quyết định nó đã lỗi thời và sau đó XÓA cùng một URI - bởi vì đó là một định danh. sau đó đi ngược lại với ngữ nghĩa HTTP. Bạn đang phát 2 hoặc nhiều URI cho mỗi tài nguyên trong đó 1 sẽ làm.)

Bây giờ, mánh gian lận là thế này: không có định nghĩa rõ ràng thực sự về tài nguyên là gì và không phải là tài nguyên, vì vậy, cách tránh phổ biến trong REST là định nghĩa một "danh từ xử lý" và trỏ URI tới đó. Đó là khá nhiều trò chơi chữ, nhưng nó đáp ứng ngữ nghĩa.

Vì vậy, nếu, ví dụ, bạn thực sự không thể sử dụng điều này vì một số lý do:

DELETE /items/123

bạn có thể tuyên bố với thế giới rằng bạn có tài nguyên xử lý "xóa" và sử dụng

POST /items/deletor  { id: 123 }

Bây giờ, nó trông rất giống RPC (Cuộc gọi thủ tục từ xa), nhưng nó rơi vào lỗ hổng lớn của mệnh đề "xử lý dữ liệu" của đặc tả POST có tên trong thông số HTTP.

Tuy nhiên, việc đó là một điều đặc biệt và nếu bạn có thể sử dụng PUT chung để tạo / cập nhật, XÓA để xóa và POST để chắp thêm, tạo và mọi thứ khác, thì bạn nên sử dụng HTTP theo tiêu chuẩn hơn. Nhưng nếu bạn có một trường hợp khó khăn như "cam kết" hoặc "xuất bản" hoặc "tái cấu trúc", thì trường hợp sử dụng danh từ bộ xử lý đáp ứng các trình lọc REST và vẫn cung cấp cho bạn ngữ nghĩa bạn cần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.