API REST - tại sao lại sử dụng PUT DELETE POST GET?


155

Vì vậy, tôi đã xem qua một số bài viết về việc tạo API REST. Và một số trong số họ đề xuất sử dụng tất cả các loại yêu cầu HTTP: như PUT DELETE POST GET. Chúng tôi sẽ tạo ra ví dụ index.php và viết API theo cách này:

$method = $_SERVER['REQUEST_METHOD'];
$request = split("/", substr(@$_SERVER['PATH_INFO'], 1));

switch ($method) {
  case 'PUT':
    ....some put action.... 
    break;
  case 'POST':
    ....some post action.... 
    break;
  case 'GET':
    ....some get action.... 
    break;
  case 'DELETE':
    ....some delete action.... 
    break;
}

OK, được cấp - Tôi không biết nhiều về các dịch vụ web (chưa). Nhưng, sẽ không dễ dàng hơn khi chỉ chấp nhận đối tượng JSON thông qua POSThoặcGET (có chứa tên phương thức và tất cả các tham số) và sau đó cũng trả lời trong JSON. Chúng ta có thể dễ dàng sắp đặt từng hàng / deserialize qua của PHP json_encode()json_decode()và làm bất cứ điều gì chúng ta muốn với dữ liệu đó mà không cần phải đối phó với các phương pháp khác nhau yêu cầu HTTP.

Tui bỏ lỡ điều gì vậy?

CẬP NHẬT 1:

Ok - sau khi đào qua nhiều API khác nhau và tìm hiểu rất nhiều về XML-RPC , JSON-RPC , SOAP , REST Tôi đã đi đến kết luận rằng loại API này là âm thanh. Trên thực tế, trao đổi ngăn xếp khá nhiều khi sử dụng phương pháp này trên trang web của họ và tôi nghĩ rằng những người này biết họ đang làm gì với API trao đổi .


4
Tại sao buộc một tải trọng JSON? Điều gì xảy ra nếu không có JSON và đó là một GET cũ đơn giản?
Mike DeSimone

Câu trả lời:


200

Ý tưởng của RE trình bày S tate T ransfer không phải là về việc truy cập dữ liệu theo cách đơn giản nhất có thể.

Bạn đã đề xuất sử dụng các yêu cầu bài đăng để truy cập JSON, đây là một cách hoàn toàn hợp lệ để truy cập / thao tác dữ liệu.

REST là một phương pháp để truy cập dữ liệu có ý nghĩa . Khi bạn thấy một yêu cầu trong REST, nó sẽ ngay lập tức thông báo những gì đang xảy ra với dữ liệu.

Ví dụ:

GET: /cars/make/chevrolet

có khả năng sẽ trả về một danh sách những chiếc xe chevy. Một api REST tốt thậm chí có thể kết hợp một số tùy chọn đầu ra trong chuỗi truy vấn như ?output=jsonhoặc ?output=htmlđiều này sẽ cho phép người truy cập quyết định định dạng nào thông tin sẽ được mã hóa.

Sau một chút suy nghĩ về làm thế nào để gõ dữ liệu một cách hợp lý kết hợp vào một API REST, tôi đã kết luận rằng cách tốt nhất để xác định kiểu dữ liệu một cách rõ ràng sẽ được thông qua phần mở rộng tập tin đã tồn tại như .js, .json, .html, hoặc .xml. Một phần mở rộng tệp bị thiếu sẽ mặc định thành bất kỳ định dạng nào là mặc định (chẳng hạn như JSON); một phần mở rộng tập tin không được hỗ trợ có thể trả về 501 Not Implementedmã trạng thái .

Một vi dụ khac:

POST: /cars/
{ make:chevrolet, model:malibu, colors:[red, green, blue, grey] }

có khả năng sẽ tạo ra một malibu chevy mới trong db với các màu liên quan. Tôi nói có khả năng vì api REST không cần liên quan trực tiếp đến cấu trúc cơ sở dữ liệu. Nó chỉ là một giao diện che giấu để dữ liệu thực được bảo vệ (nghĩ về nó giống như các bộ truy cập và bộ biến đổi cho cấu trúc cơ sở dữ liệu).

Bây giờ chúng ta cần chuyển sang vấn đề bình đẳng . Thông thường REST thực hiện CRUD trên HTTP. HTTP sử dụng GET, PUT, POSTDELETE cho các yêu cầu.

Việc triển khai REST rất đơn giản có thể sử dụng ánh xạ CRUD sau:

Create -> Post
Read   -> Get
Update -> Put
Delete -> Delete

Có một vấn đề với việc thực hiện này: Bài đăng được định nghĩa là một phương pháp không bình thường. Điều này có nghĩa là các cuộc gọi tiếp theo của cùng một phương thức Post sẽ dẫn đến các trạng thái máy chủ khác nhau . Nhận, đặt và xóa, là idempotent; có nghĩa là gọi chúng nhiều lần sẽ dẫn đến trạng thái máy chủ giống hệt nhau.

Điều này có nghĩa là một yêu cầu như:

Delete: /cars/oldest

thực sự có thể được thực hiện như:

Post: /cars/oldest?action=delete

Trong khi

Delete: /cars/id/123456

sẽ dẫn đến trạng thái máy chủ tương tự nếu bạn gọi nó một lần hoặc nếu bạn gọi nó 1000 lần.

Một cách tốt hơn để xử lý loại bỏ các oldestmặt hàng sẽ được yêu cầu:

Get: /cars/oldest

và sử dụng IDtừ dữ liệu kết quả để đưa ra deleteyêu cầu:

Delete: /cars/id/[oldest id]

Một vấn đề với phương pháp này sẽ là nếu một /carsmục khác được thêm vào giữa khi /oldestđược yêu cầu và khi mục deleteđược ban hành.


3
@Andre đó là sự kết hợp của một số lý do: Thực hiện theo các nguyên tắc HTTP có nghĩa là bạn (có thể) sẽ có ít vấn đề tương thích ngược hơn khi mọi thứ thay đổi; sử dụng một hình thức html thông qua POST sẽ cảnh báo người dùng về nhiều lần gửi cùng một dữ liệu (điều này là để ngăn chặn một giao dịch không có chủ đích); theo một thực hành tốt nhất được xác định rõ là, tốt .. thực hành tốt. Phần còn lại không được xác định với một triển khai cụ thể, cho phép bạn sử dụng nó khi bạn thấy phù hợp. Tôi khuyên bạn nên tận dụng tất cả các mã lỗi và phương thức yêu cầu của HTTP, nhưng bạn được phép làm điều đó theo cách bạn muốn
zzzzBov

4
Vì vậy, vấn đề với câu trả lời này (đó là một câu trả lời đúng, nhưng không đầy đủ) là nó không giải quyết được câu hỏi chính mà anh ấy đã hỏi: Tại sao bạn sử dụng động từ HTTP và URI thay vì dữ liệu JSON tùy chỉnh (có thể là một số loại Cú pháp gọi API dựa trên JSON). Bạn có thể tạo cú pháp JSON tùy chỉnh của mình để nó "ngay lập tức ... thông báo những gì đang xảy ra với dữ liệu". Những gì bạn không thể làm là dễ dàng sử dụng các tiện ích và lớp mạng tích hợp trên HTTP như bạn có thể với một API tuân theo tất cả các quy ước REST. Tất nhiên, không phải câu trả lời của tôi là hoàn hảo;)
Merlyn Morgan-Graham

4
@Andre: Các ví dụ mà mục nhập wiki sử dụng là xác thực, lưu trữ và thương lượng loại nội dung. Bây giờ tôi đang suy nghĩ nhiều hơn về nó, bạn có thể sử dụng chúng với các giao diện kiểu RPC, nhưng sự cám dỗ thường sẽ là thực hiện hệ thống của riêng bạn từ đầu, hoặc mã hóa tích hợp vào hệ thống hiện có. Với REST, bạn có thể sử dụng tích hợp tích hợp và quản trị nó trên máy chủ web. Điều này có nghĩa là khớp nối lỏng lẻo hơn, có nghĩa là bạn phải thực hiện ít hơn và có nghĩa là ứng dụng của bạn linh hoạt hơn để thay đổi các tùy chọn trong tương lai với mã ít hơn và tác động thử nghiệm.
Merlyn Morgan-Graham

10
Thay vì XÓA: / ô tô / cũ nhất, làm thế nào để NHẬN: / ô tô / cũ nhất theo sau là XÓA? Bằng cách đó, bạn có hai lệnh idempotent riêng biệt.
Neil

3
+1; Tôi đồng ý rằng đây là một câu trả lời hay (Tôi sẽ xem lại để giải trí và kiếm lợi nhuận). POST: /cars/oldestlà một sự thay thế cho một XÓA không có nhiều ý nghĩa. Một cái gì đó như - POST: /cars/oldest/deletecó thể, tho Tôi nghĩ rằng tôi thích giải pháp của Neil hơn. Ưu điểm duy nhất mà việc xóa trực tiếp mang lại cho giải pháp get-id-xóa-id của anh ấy là tính nguyên tử. Tôi muốn có một sự biện minh kinh doanh rõ ràng với một kịch bản không có kế hoạch trước khi tôi thực hiện một điều như vậy. Bạn không cần phải hỗ trợ tất cả các động từ trên tất cả các đối tượng / url.
Merlyn Morgan-Graham

39

Đây là một câu hỏi bảo mật và bảo trì.

phương pháp an toàn

Bất cứ khi nào có thể, bạn nên sử dụng các phương pháp 'an toàn' (một chiều) như GET và HEAD để hạn chế lỗ hổng tiềm ẩn.

phương pháp tạm thời

Bất cứ khi nào có thể, bạn nên sử dụng các phương pháp 'idempotent' như GET, HEAD, PUT và DELETE, không thể có tác dụng phụ và do đó ít bị lỗi / dễ kiểm soát hơn.

Nguồn


1
Xin lỗi, nhưng các phương thức idempotent PUT và DELETE như thế nào? Chúng ảnh hưởng đến trạng thái của máy chủ và dữ liệu của nó!
Mahmoud Al-Qudsi

27
@ Máy tính: Thực hiện cùng một PUT hoặc cùng một kết quả XÓA trong cùng một trạng thái cuối cùng. Đó là những gì "idempotent" có nghĩa là.
Ignacio Vazquez-Abrams

4
Để làm rõ hơn: một hoạt động F là idempotent, nếu ứng dụng đơn lẻ và một số ứng dụng hệ quả của nó đều trả về cùng một kết quả. Chính xác hơn F là idempotent khi và chỉ khi F (x) = F (F (x)). Ví dụ: Xóa là idempotent, vì khi bạn xóa một mục một lần hoặc xóa nó nhiều lần, kết quả là như nhau: mục đó bị xóa chỉ một lần với ứng dụng xóa đầu tiên và không có gì xảy ra trong ứng dụng xóa thứ hai hoặc thứ ba.
qartal

1
Nhưng về mặt tạo, khi bạn tạo một bản ghi mới bằng lệnh tạo và phát lại cùng một lệnh, hai bản ghi được (có thể) được tạo (mặc dù cả hai đều phản ánh cùng một thông tin).
qartal

qartal - định nghĩa chức năng của bạn cho idempotent phải là 'F (X) = F (X) F (X)'. Cách tốt đẹp để cụm từ mặc dù.
Gerard ONeill

26

Nói tóm lại, REST nhấn mạnh danh từ hơn động từ. Khi API của bạn trở nên phức tạp hơn, bạn thêm nhiều thứ hơn là nhiều lệnh hơn.


2
Tôi đã có một chút rắc rối khi đi vòng này. Bài đăng này ( lornajane.net/posts/2013/ ( )) rằng động từ nên xuất phát từ yêu cầu HTTP để URI chỉ nên chứa các danh từ đã xóa nó một chút cho tôi
icc97

9

Bạn hỏi :

Sẽ không dễ dàng hơn khi chỉ chấp nhận đối tượng JSON thông qua $ _POST bình thường và sau đó cũng trả lời bằng JSON

Từ Wikipedia trên REST :

Các ứng dụng RESTful tối đa hóa việc sử dụng giao diện được xác định trước, được xác định rõ và các khả năng tích hợp sẵn khác được cung cấp bởi giao thức mạng đã chọn và giảm thiểu việc bổ sung các tính năng dành riêng cho ứng dụng mới trên đầu trang

Từ những gì (ít) tôi đã thấy, tôi tin rằng điều này thường được thực hiện bằng cách tối đa hóa việc sử dụng các động từ HTTP hiện có và thiết kế một lược đồ URL cho dịch vụ của bạn mạnh mẽ và rõ ràng nhất có thể.

Các giao thức dữ liệu tùy chỉnh (ngay cả khi chúng được xây dựng dựa trên các giao thức chuẩn, chẳng hạn như SOAP hoặc JSON) không được khuyến khích và nên được giảm thiểu để phù hợp nhất với hệ tư tưởng REST.

Mặt khác, SOAP RPC qua HTTP, khuyến khích mỗi nhà thiết kế ứng dụng xác định một từ vựng mới và tùy ý của các danh từ và động từ (ví dụ getUsers (), savePurchaseOrder (...)), thường được phủ lên động từ 'POST' của HTTP. Điều này bỏ qua nhiều khả năng hiện có của HTTP như xác thực, lưu trữ bộ đệm và đàm phán loại nội dung và có thể khiến nhà thiết kế ứng dụng phát minh lại nhiều tính năng này trong từ vựng mới.

Các đối tượng thực tế bạn đang làm việc có thể ở bất kỳ định dạng nào. Ý tưởng là sử dụng lại càng nhiều HTTP càng tốt để hiển thị các hoạt động của bạn mà người dùng muốn thực hiện trên các tài nguyên đó (truy vấn, quản lý / đột biến, xóa).

Bạn hỏi :

Tui bỏ lỡ điều gì vậy?

Có rất nhiều điều để biết về REST và chính cú pháp URI / động từ HTTP. Ví dụ, một số động từ là idempotent, một số khác thì không. Tôi không thấy gì về điều này trong câu hỏi của bạn, vì vậy tôi không buồn cố gắng đi sâu vào nó. Các câu trả lời khác và Wikipedia đều có rất nhiều thông tin tốt.

Ngoài ra, có rất nhiều điều để tìm hiểu về các công nghệ mạng khác nhau được xây dựng dựa trên HTTP mà bạn có thể tận dụng nếu bạn đang sử dụng API thực sự yên tĩnh. Tôi sẽ bắt đầu với xác thực.


8

Liên quan đến việc sử dụng phần mở rộng để xác định loại dữ liệu. Tôi nhận thấy rằng API MailChimp đang làm điều đó, nhưng tôi không nghĩ rằng đây là một ý tưởng hay.

GET /zzz/cars.json/1

GET /zzz/cars.xml/1

Nghe có vẻ là một ý tưởng hay, nhưng tôi nghĩ cách tiếp cận "cũ hơn" là tốt hơn - sử dụng các tiêu đề HTTP

GET /xxx/cars/1
Accept: application/json

Ngoài ra các tiêu đề HTTP tốt hơn nhiều cho giao tiếp kiểu dữ liệu chéo (nếu có ai đó sẽ cần nó)

POST /zzz/cars
Content-Type: application/xml     <--- indicates we sent XML to server
Accept: application/json          <--- indicates we want get data back in JSON format  

4

Tui bỏ lỡ điều gì vậy?

Đúng. ;-)

Hiện tượng này tồn tại do hạn chế giao diện thống nhất . REST thích sử dụng các tiêu chuẩn đã có sẵn thay vì phát minh lại bánh xe. Tiêu chuẩn HTTP đã được chứng minh là có khả năng mở rộng cao (web đang hoạt động được một thời gian). Tại sao chúng ta nên sửa cái gì đó không bị hỏng?!

lưu ý: Ràng buộc giao diện thống nhất rất quan trọng nếu bạn muốn tách rời các máy khách khỏi dịch vụ. Nó tương tự như việc xác định các giao diện cho các lớp để tách chúng ra khỏi nhau. Tất nhiên ở đây giao diện thống nhất bao gồm các tiêu chuẩn như HTTP , các loại MIME , URI , RDF , vocab dữ liệu được liên kết , hydra vocab , v.v ...


2

Tốt ngữ nghĩa là quan trọng trong lập trình.

Việc sử dụng nhiều phương thức hơn ngoài GET / POST sẽ hữu ích vì nó sẽ tăng khả năng đọc mã của bạn và giúp bảo trì dễ dàng hơn.

Tại sao?

Bởi vì bạn biết GET sẽ lấy dữ liệu từ api của bạn. Bạn biết POST sẽ thêm dữ liệu mới vào hệ thống của bạn. Bạn biết PUT sẽ thực hiện cập nhật. XÓA sẽ xóa các hàng, v.v.

Tôi thường cấu trúc các dịch vụ web RESTFUL của mình để tôi có một hàm gọi lại có tên giống như phương thức.

Tôi sử dụng PHP, vì vậy tôi sử dụng function_exists (tôi nghĩ nó được gọi là). Nếu chức năng không tồn tại, tôi ném 405 (PHƯƠNG PHÁP KHÔNG ĐƯỢC PHÉP).


2

Bill Venners: Trong bài đăng trên blog của bạn có tên "Tại sao REST không thành công", bạn đã nói rằng chúng tôi cần tất cả bốn động từ HTTP GET GET, POST, PUT và DELETE và than thở rằng các nhà cung cấp trình duyệt chỉ GET và POST. "Tại sao chúng ta cần cả bốn động từ? Tại sao không NHẬN và POST đủ?

Elliotte Rusty Harold: Có bốn phương thức cơ bản trong HTTP: GET, POST, PUT và DELETE. GET được sử dụng hầu hết thời gian. Nó được sử dụng cho bất cứ điều gì an toàn, không gây ra tác dụng phụ. GET có thể được đánh dấu, lưu trữ, liên kết đến, thông qua một máy chủ proxy. Đó là một hoạt động rất mạnh mẽ, một hoạt động rất hữu ích.

POST bằng cách tương phản có lẽ là hoạt động mạnh mẽ nhất. Nó có thể làm bất cứ điều gì. Không có giới hạn về những gì có thể xảy ra, và kết quả là, bạn phải rất cẩn thận với nó. Bạn không đánh dấu nó. Bạn không lưu trữ nó. Bạn không lấy trước nó. Bạn không làm gì với POST mà không hỏi người dùng. Bạn có muốn làm điều này? Nếu người dùng nhấn nút, bạn có thể POST một số nội dung. Nhưng bạn sẽ không nhìn vào tất cả các nút trên một trang và bắt đầu nhấn ngẫu nhiên chúng. Ngược lại, các trình duyệt có thể xem tất cả các liên kết trên trang và tìm nạp trước chúng hoặc tìm nạp trước các liên kết mà chúng nghĩ là có khả năng sẽ được theo dõi tiếp theo. Và trên thực tế, một số trình duyệt và tiện ích mở rộng Firefox và nhiều công cụ khác đã cố gắng làm điều đó ở điểm này hay điểm khác.

PUT và DELETE nằm ở giữa giữa GET và POST. Sự khác biệt giữa PUT hoặc DELETE và POST là PUT và DELETE là * idempotent, trong khi POST thì không. PUT và DELETE có thể được lặp lại nếu cần thiết. Giả sử bạn đang cố tải lên một trang mới lên một trang web. Giả sử bạn muốn tạo một trang mới tại http://www.example.com/foo.html, vì vậy bạn nhập nội dung của bạn và bạn đặt nó tại URL đó. Máy chủ tạo trang đó tại URL mà bạn cung cấp. Bây giờ, giả sử vì một số lý do kết nối mạng của bạn bị hỏng. Bạn không chắc chắn, yêu cầu có được thông qua hay không? Có lẽ mạng chậm. Có lẽ đã có một vấn đề máy chủ proxy. Vì vậy, hoàn toàn ổn khi thử lại, hoặc một lần nữa, bao nhiêu lần tùy thích. Bởi vì PUTTING cùng một tài liệu cho cùng một URL mười lần sẽ không khác gì đặt nó một lần. Điều này cũng đúng với XÓA. Bạn có thể XÓA một cái gì đó mười lần, và điều đó giống như xóa nó một lần.

Ngược lại, POST, có thể gây ra một cái gì đó khác nhau xảy ra mỗi lần. Hãy tưởng tượng bạn đang kiểm tra một cửa hàng trực tuyến bằng cách nhấn nút mua. Nếu bạn gửi lại yêu cầu POST đó, cuối cùng bạn có thể mua mọi thứ trong giỏ hàng của mình. Nếu bạn gửi lại, bạn đã mua nó lần thứ ba. Đó là lý do tại sao các trình duyệt phải rất cẩn thận về việc lặp lại các thao tác POST mà không có sự đồng ý rõ ràng của người dùng, bởi vì POST có thể gây ra hai điều xảy ra nếu bạn thực hiện hai lần, ba điều nếu bạn thực hiện ba lần. Với PUT và DELETE, có một sự khác biệt lớn giữa 0 yêu cầu và một yêu cầu, nhưng không có sự khác biệt giữa một yêu cầu và mười yêu cầu.

Vui lòng truy cập url để biết thêm chi tiết. http://www.artima.com/lejava/articles/why_put_and_delete.html

Cập nhật:

Các phương thức idempotent Một phương thức HTTP idempotent là một phương thức HTTP có thể được gọi nhiều lần mà không có kết quả khác nhau. Sẽ không có vấn đề gì nếu phương thức chỉ được gọi một lần, hoặc hơn mười lần. Kết quả phải giống nhau. Một lần nữa, điều này chỉ áp dụng cho kết quả, không phải bản thân tài nguyên. Điều này vẫn có thể được thao tác (như dấu thời gian cập nhật, miễn là thông tin này không được chia sẻ trong biểu diễn tài nguyên (hiện tại).

Hãy xem xét các ví dụ sau:

a = 4;

a ++;

Ví dụ đầu tiên là idempotent: cho dù chúng ta có thực thi câu lệnh này bao nhiêu lần đi chăng nữa thì a sẽ luôn là 4. Ví dụ thứ hai không phải là idempotent. Thực hiện điều này 10 lần sẽ dẫn đến một kết quả khác như khi chạy 5 lần. Vì cả hai ví dụ đang thay đổi giá trị của a, cả hai đều là phương pháp không an toàn.


1
Về ví dụ về một trang mới, không nên sử dụng POST theo cách đó, trong khi PUT cho một bản cập nhật? Tạo một trang mới là một quá trình luôn luôn tạo ra một kết quả mới, trong khi chỉnh sửa tương tự có thể được đặt lại bất kỳ số lượng thời gian nào, luôn mang lại kết quả tương tự. Phrasing tốt đẹp và giải thích, mặc dù.
Spyryto

0

Về cơ bản REST là ( wiki ):

  1. Kiến trúc máy chủ của khách hàng
  2. Không quốc tịch
  3. Tính dễ thấm
  4. Hệ thống lớp
  5. Mã theo yêu cầu (tùy chọn)
  6. Giao diện thống nhất

REST không phải là giao thức, nó là nguyên tắc. Uris và phương pháp khác nhau - ai đó được gọi là thực hành tốt nhất.

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.