PUT so với POST trong REST


5373

Theo Thông số HTTP / 1.1:

Các POSTphương pháp được sử dụng để yêu cầu rằng các máy chủ gốc chấp nhận các thực thể khép kín trong yêu cầu như một cấp dưới mới của tài nguyên được xác định bởi Request-URItrongRequest-Line

Nói cách khác, POSTđược sử dụng để tạo ra .

Các PUTyêu cầu phương pháp mà các thực thể kèm được lưu trữ dưới cung cấp Request-URI. Nếu tham chiếu Request-URIđến một tài nguyên đã tồn tại, thực thể kèm theo NÊN được coi là phiên bản sửa đổi của tài nguyên cư trú trên máy chủ gốc. Nếu Request-URIkhông trỏ đến một tài nguyên hiện có và URI đó có khả năng được xác định là tài nguyên mới bởi tác nhân người dùng yêu cầu, máy chủ gốc có thể tạo tài nguyên với URI đó. "

Đó là, PUTđược sử dụng để tạo hoặc thay thế .

Vì vậy, cái nào nên được sử dụng để tạo ra một tài nguyên? Hoặc một người cần phải hỗ trợ cả hai?


56
Có thể hữu ích khi sử dụng các định nghĩa trong HTTPbis - Roy đã đưa ra một lượng công việc hợp lý để làm rõ chúng. Xem: tools.ietf.org/html/ Khăn
Mark Nottingham

16
Chỉ cần đưa nhận xét của @ MarkNottingham vào phiên bản mới nhất, đây là POSTPUT , như được định nghĩa trên HTTPbis.
Marius Butuc

37
Dường như với tôi rằng cuộc tranh luận này đã nảy sinh từ thực tiễn phổ biến của việc đơn giản hóa quá mức REST bằng cách mô tả các Phương thức HTTP theo các hoạt động CRUD.
Stuporman

5
Thật không may các câu trả lời đầu tiên là sai về POST. Kiểm tra câu trả lời của tôi để được giải thích rõ hơn về sự khác biệt: stackoverflow.com/a/18243587/2458234
7hi4g0

23
PUT và POST đều là các phương thức không an toàn. Tuy nhiên, PUT là idempotent, trong khi POST thì không. - Xem thêm tại: restcookbook.com/HTTP%20Methods/put-vs-post/iêu
Dinesh Saini

Câu trả lời:


4239

Nhìn chung:

Cả PUT và POST đều có thể được sử dụng để tạo.

Bạn phải hỏi "bạn đang thực hiện hành động để làm gì?" để phân biệt những gì bạn nên sử dụng. Giả sử bạn đang thiết kế một API để đặt câu hỏi. Nếu bạn muốn sử dụng POST thì bạn sẽ làm điều đó với một danh sách các câu hỏi. Nếu bạn muốn sử dụng PUT thì bạn sẽ làm điều đó cho một câu hỏi cụ thể.

Cả hai đều có thể được sử dụng, vì vậy tôi nên sử dụng cái nào trong thiết kế RESTful của mình:

Bạn không cần phải hỗ trợ cả PUT và POST.

Mà được sử dụng là tùy thuộc vào bạn. Nhưng chỉ cần nhớ sử dụng đúng tùy thuộc vào đối tượng bạn đang tham chiếu trong yêu cầu.

Một số cân nhắc:

  • Bạn có đặt tên rõ ràng cho các đối tượng URL mà bạn tạo hoặc để máy chủ quyết định không? Nếu bạn đặt tên cho chúng thì hãy sử dụng PUT. Nếu bạn để máy chủ quyết định thì hãy sử dụng POST.
  • PUT là idempotent, vì vậy nếu bạn PUT một đối tượng hai lần, nó không có hiệu lực. Đây là một tài sản tốt, vì vậy tôi sẽ sử dụng PUT khi có thể.
  • Bạn có thể cập nhật hoặc tạo tài nguyên bằng PUT với cùng một URL đối tượng
  • Với POST, bạn có thể có 2 yêu cầu đến cùng lúc thực hiện sửa đổi cho một URL và chúng có thể cập nhật các phần khác nhau của đối tượng.

Một ví dụ:

Tôi đã viết như sau như một phần của câu trả lời khác về SO về điều này :

BÀI ĐĂNG:

Được sử dụng để sửa đổi và cập nhật tài nguyên

POST /questions/<existing_question> HTTP/1.1
Host: www.example.com/

Lưu ý rằng sau đây là một lỗi:

POST /questions/<new_question> HTTP/1.1
Host: www.example.com/

Nếu URL chưa được tạo, bạn không nên sử dụng POST để tạo URL trong khi chỉ định tên. Điều này sẽ dẫn đến lỗi 'không tìm thấy tài nguyên' vì <new_question>chưa tồn tại. Bạn nên PUT <new_question> tài nguyên trên máy chủ trước.

Mặc dù bạn có thể làm một cái gì đó như thế này để tạo tài nguyên bằng POST:

POST /questions HTTP/1.1
Host: www.example.com/

Lưu ý rằng trong trường hợp này tên tài nguyên không được chỉ định, đường dẫn URL của đối tượng mới sẽ được trả về cho bạn.

ĐẶT:

Được sử dụng để tạo một tài nguyên, hoặc ghi đè lên nó. Trong khi bạn chỉ định tài nguyên URL mới.

Đối với tài nguyên mới:

PUT /questions/<new_question> HTTP/1.1
Host: www.example.com/

Để ghi đè một tài nguyên hiện có:

PUT /questions/<existing_question> HTTP/1.1
Host: www.example.com/

Ngoài ra, và chính xác hơn một chút, RFC 7231 Mục 4.3.4 trạng thái PUT (nhấn mạnh thêm),

4.3.4. ĐẶT

Phương thức PUT yêu cầu trạng thái của tài nguyên đích phải createdhoặc replacedvới trạng thái được xác định bởi biểu diễn kèm theo trong tải trọng thông báo yêu cầu.


1026
Tôi nghĩ rằng người ta không thể nhấn mạnh đủ thực tế rằng PUT là không có giá trị: nếu mạng bị lỗi và khách hàng không chắc chắn liệu yêu cầu của mình có được thực hiện hay không, nó có thể gửi lần thứ hai (hoặc 100) và nó được đảm bảo bởi HTTP spec rằng điều này có tác dụng chính xác như gửi một lần.
Jörg W Mittag 10/03/2016

77
@ Jorg W Mittag: Không cần thiết. Lần thứ hai có thể trả lại 409 Xung đột hoặc một cái gì đó nếu yêu cầu đã được sửa đổi trong thời gian đó (bởi một số người dùng khác hoặc chính yêu cầu đầu tiên, đã thông qua).
Mitar

632
Nếu tôi không nhầm, điều chúng ta nên nhấn mạnh là PUT được định nghĩa là không hoạt động. Bạn vẫn phải viết máy chủ của mình theo cách PUT hoạt động chính xác, đúng chứ? Có lẽ tốt hơn nên nói "PUT làm cho việc vận chuyển giả định tính không ổn định, điều này có thể ảnh hưởng đến hành vi của việc vận chuyển, ví dụ như bộ nhớ đệm."
Ian Ni-Lewis

150
@ JörgWMittag Câu khẩu hiệu bắt chước? Làm thế nào về "Gửi và gửi và gửi bạn của tôi, nó không có sự khác biệt cuối cùng."
James Beninger

39
Nghĩ về chúng như: PUT = insert hoặc update; POST = chèn. Vì vậy, khi bạn thực hiện hai PUT - bạn nhận được một bản ghi mới, khi bạn thực hiện hai POST - bạn nhận được hai bản ghi mới.
Eugen Konkov

2218

Bạn có thể tìm thấy các xác nhận trên web cho biết

Không phải là hoàn toàn đúng.


Tốt hơn là chọn giữa PUT và POST dựa trên tính không thay đổi của hành động.

PUT ngụ ý đặt một tài nguyên - thay thế hoàn toàn bất cứ thứ gì có sẵn tại URL đã cho bằng một thứ khác. Theo định nghĩa, PUT là idempotent. Làm điều đó nhiều lần như bạn muốn, và kết quả là như nhau.x=5là bình thường. Bạn có thể PUT một tài nguyên cho dù trước đó nó có tồn tại hay không (ví dụ: để Tạo hoặc Cập nhật)!

POST cập nhật tài nguyên, thêm tài nguyên phụ hoặc gây ra thay đổi. POST không phải là idempotent, theo cách x++không phải là idempotent.


Theo lập luận này, PUT là để tạo khi bạn biết URL của thứ bạn sẽ tạo. POST có thể được sử dụng để tạo khi bạn biết URL của "nhà máy" hoặc người quản lý cho danh mục những thứ bạn muốn tạo.

vì thế:

POST /expense-report

hoặc là:

PUT  /expense-report/10929

72
Tôi đồng ý, bất cứ nơi nào có liên quan đến sự bình tĩnh, nó sẽ tạo ra bất kỳ mối lo ngại nào khác vì việc nhận sai có thể gây ra nhiều lỗi không mong muốn.
Josh

16
Nếu POST có thể cập nhật một tài nguyên, làm thế nào mà nó không phải là idempotent? Nếu tôi thay đổi tuổi học sinh bằng PUT và làm điều đó gấp 10 lần tuổi học sinh là như nhau nếu tôi làm điều đó một lần.
Jack Ukleja

28
@Schneider, trong trường hợp này, máy chủ của bạn đang nỗ lực thêm để đảm bảo sự bình tĩnh, nhưng nó không quảng cáo nó. Các trình duyệt vẫn sẽ cảnh báo người dùng nếu họ cố tải lại yêu cầu POST như vậy.
Tobu

47
@Schneider POST có thể tạo tài nguyên phụ; do đó bạn có thể POST để thu thập, như POST / báo cáo chi phí và nó sẽ tạo ra nhiều thực thể (báo cáo chi phí) trên máy chủ của bạn theo số lượng yêu cầu bạn đã gửi, ngay cả khi chúng hoàn toàn giống nhau. Hãy nghĩ về nó như chèn cùng một hàng trong bảng DB (/ báo cáo chi phí) với khóa chính tăng tự động. Dữ liệu vẫn giữ nguyên, khóa (URI trong trường hợp này) được tạo bởi máy chủ và khác nhau cho mỗi lần chèn (yêu cầu) khác. Vì vậy, hiệu ứng POST có thể là idempotent, nhưng cũng có thể không. Do đó, POST không phải là idempotent.
Snifff

11
Giả sử chúng ta có các thực thể có thể có hai thuộc tính - namedate. Nếu chúng ta có một thực thể có sẵn namedate, nhưng sau đó chỉ yêu cầu nó chỉ định a name, thì hành vi đúng của PUT sẽ là xóa sạch datethực thể đó, trong khi POST chỉ có thể cập nhật các thuộc tính được chỉ định, để lại các thuộc tính không xác định như chúng trước khi yêu cầu được thực hiện. Điều đó nghe có vẻ đúng / hợp lý hay là việc sử dụng PUT không đúng cách (tôi đã thấy các tài liệu tham khảo về PATCH , có vẻ như nó sẽ phù hợp hơn, nhưng vẫn chưa tồn tại)?
Jon z

707
  • POST vào URL tạo tài nguyên con tại URL do máy chủ xác định .
  • PUT cho một URL tạo / thay thế toàn bộ tài nguyên tại URL do khách hàng xác định .
  • VĂN tới một URL cập nhật một phần tài nguyên tại URL do khách hàng đó xác định.

Thông số kỹ thuật có liên quan cho PUT và POST là RFC 2616 §9.5ff.

POST tạo một tài nguyên con , vì vậy POST để /itemstạo một tài nguyên nằm dưới /itemstài nguyên đó. Ví dụ. /items/1. Gửi cùng một gói bài hai lần sẽ tạo ra hai tài nguyên.

PUT là để tạo hoặc thay thế tài nguyên tại một URL được khách hàng biết đến .

Do đó: PUT chỉ là ứng cử viên cho TẠO nơi khách hàng đã biết url trước khi tài nguyên được tạo. Ví dụ. /blogs/nigel/entry/when_to_use_post_vs_putnhư tiêu đề được sử dụng làm khóa tài nguyên

PUT thay thế tài nguyên tại url đã biết nếu nó đã tồn tại, do đó, việc gửi cùng một yêu cầu hai lần không có hiệu lực. Nói cách khác, các cuộc gọi đến PUT là tạm thời .

RFC đọc như thế này:

Sự khác biệt cơ bản giữa các yêu cầu POST và PUT được phản ánh theo nghĩa khác nhau của URI yêu cầu. URI trong yêu cầu POST xác định tài nguyên sẽ xử lý thực thể kèm theo. Tài nguyên đó có thể là một quá trình chấp nhận dữ liệu, một cổng vào một số giao thức khác hoặc một thực thể riêng biệt chấp nhận các chú thích. Ngược lại, URI trong yêu cầu PUT xác định thực thể kèm theo yêu cầu - tác nhân người dùng biết URI dự định là gì và máy chủ KHÔNG cố gắng áp dụng yêu cầu cho một số tài nguyên khác. Nếu máy chủ mong muốn rằng yêu cầu được áp dụng cho một URI khác,

Lưu ý: PUT chủ yếu được sử dụng để cập nhật tài nguyên (bằng cách thay thế toàn bộ tài nguyên), nhưng gần đây có xu hướng sử dụng PATCH để cập nhật tài nguyên hiện có, vì PUT chỉ định rằng nó thay thế toàn bộ tài nguyên. RFC 5789.

Cập nhật 2018 : Có một trường hợp có thể được thực hiện để tránh PUT. Xem "REST không PUT"

Với hệ thống REST REST không có kỹ thuật PUT, ý tưởng là người tiêu dùng buộc phải đăng tài nguyên yêu cầu 'danh từ' mới. Như đã thảo luận trước đó, việc thay đổi địa chỉ gửi thư của khách hàng là POST thành tài nguyên mới ChangeOfAddress, chứ không phải là PUT của tài nguyên khách hàng trên mạng với giá trị trường địa chỉ gửi thư khác.

lấy từ Thiết kế API REST - Mô hình hóa tài nguyên bởi Prakash Subramaniam của Th Thinkworks

Điều này buộc API phải tránh các vấn đề chuyển trạng thái với nhiều khách hàng đang cập nhật một tài nguyên và phù hợp hơn với nguồn cung cấp sự kiện và CQRS. Khi công việc được thực hiện không đồng bộ, BÀI ĐĂNG chuyển đổi và chờ đợi nó được áp dụng có vẻ phù hợp.


53
Hoặc từ phía bên kia của hàng rào: PUT nếu máy khách xác định địa chỉ của tài nguyên kết quả, POST nếu máy chủ thực hiện.
DanMan

3
Tôi nghĩ rằng câu trả lời này nên được chỉnh sửa để làm rõ hơn những gì @DanMan đã chỉ ra một cách rất đơn giản. Điều tôi thấy có giá trị nhất ở đây là ghi chú ở cuối, nói rằng chỉ nên sử dụng PUT để thay thế toàn bộ tài nguyên.
Hermes

3
PATCH không phải là một lựa chọn thực tế trong ít nhất một vài năm, nhưng tôi đồng ý với ý thức hệ.
nghiền nát

4
Tôi đang cố gắng để hiểu, nhưng sử dụng PUT để tạo ra thứ gì đó sẽ chỉ có ý nghĩa nếu khách hàng biết chắc rằng tài nguyên chưa tồn tại, phải không? Theo ví dụ về blog, giả sử bạn đã tạo ra hàng trăm bài đăng blog trong một vài năm, sau đó vô tình chọn tiêu đề giống như bạn đã làm cho một bài đăng hai năm trước. Bây giờ bạn đã đi và thay thế bài đăng đó, mà không có ý định. Vì vậy, sử dụng PUT để tạo sẽ yêu cầu khách hàng theo dõi những gì được thực hiện và những gì không, và có thể dẫn đến tai nạn và tác dụng phụ ngoài ý muốn, cũng như có các tuyến đường thực hiện hai điều hoàn toàn khác nhau?
galaxyAd.

5
Bạn nói đúng. PUTting một bài đăng blog ở cùng một url với một bài viết hiện tại sẽ gây ra một bản cập nhật cho bài đăng hiện có (mặc dù bạn rõ ràng có thể kiểm tra trước bằng một GET). Điều này cho thấy lý do tại sao chỉ sử dụng tiêu đề làm URL. Tuy nhiên, nó sẽ hoạt động ở bất cứ nơi nào có khóa tự nhiên trong dữ liệu ... mà theo kinh nghiệm của tôi là rất hiếm. Hoặc nếu bạn đã sử dụng GUID
Nigel Thorne

221

Tóm lược:

Tạo nên:

Có thể được thực hiện với cả PUT hoặc POST theo cách sau:

ĐẶT

Tạo THE tài nguyên mới với newResourceId như nhận diện, dưới sự tài / URI, hoặc bộ sưu tập .

PUT /resources/<newResourceId> HTTP/1.1 

BÀI ĐĂNG

Tạo Một tài nguyên mới dưới các nguồn lực / URI, hoặc bộ sưu tập . Thông thường định danh được trả về bởi máy chủ.

POST /resources HTTP/1.1

Cập nhật:

Có thể chỉ được thực hiện với PUT theo cách sau:

ĐẶT

Cập nhật tài nguyên với currentResourceId làm định danh, dưới / URI tài nguyên hoặc bộ sưu tập .

PUT /resources/<existingResourceId> HTTP/1.1

Giải trình:

Khi giao dịch với REST và URI là chung, bạn có chungbên tráicụ thểbên phải . Các tổng quát thường được gọi là các bộ sưu tập và các mục cụ thể hơn có thể được gọi là tài nguyên . Lưu ý rằng một tài nguyên có thể chứa một bộ sưu tập .

Ví dụ:

<- chung chung - cụ thể ->

URI: website.com/users/john
website.com  - whole site
users        - collection of users
john         - item of the collection, or a resource

URI:website.com/users/john/posts/23
website.com  - whole site
users        - collection of users
john         - item of the collection, or a resource
posts        - collection of posts from john
23           - post from john with identifier 23, also a resource

Khi bạn sử dụng POST, bạn luôn tham chiếu đến một bộ sưu tập , vì vậy bất cứ khi nào bạn nói:

POST /users HTTP/1.1

bạn đang đăng một người dùng mới vào bộ sưu tập người dùng .

Nếu bạn tiếp tục và thử một cái gì đó như thế này:

POST /users/john HTTP/1.1

nó sẽ hoạt động, nhưng về mặt ngữ nghĩa, bạn đang nói rằng bạn muốn thêm tài nguyên vào bộ sưu tập john trong bộ sưu tập người dùng .

Khi bạn đang sử dụng PUT, bạn đang tham chiếu đến một tài nguyên hoặc một mục duy nhất, có thể bên trong một bộ sưu tập . Vì vậy, khi bạn nói:

PUT /users/john HTTP/1.1

bạn đang nói với bản cập nhật máy chủ hoặc tạo nếu nó không tồn tại, tài nguyên john trong bộ sưu tập người dùng .

Thông số:

Hãy để tôi nhấn mạnh một số phần quan trọng của thông số kỹ thuật:

BÀI ĐĂNG

Các POST phương pháp được sử dụng để yêu cầu rằng các máy chủ gốc chấp nhận các thực thể khép kín trong yêu cầu như một mới cấp dưới của tài nguyên được xác định bởi Yêu cầu-URI trong Request-Line

Do đó, tạo ra một tài nguyên mới trên một bộ sưu tập .

ĐẶT

Các PUT yêu cầu phương pháp mà các thực thể kèm được lưu trữ dưới đã cung cấp Yêu cầu-URI. Nếu URI yêu cầu đề cập đến một tài nguyên đã tồn tại , thực thể kèm theo NÊN được coi là phiên bản sửa đổi của tài nguyên cư trú trên máy chủ gốc. Nếu URI yêu cầu không trỏ đến tài nguyên hiện có và URI đó có khả năng được xác định là tài nguyên mới bởi tác nhân người dùng yêu cầu, máy chủ gốc có thể tạo tài nguyên với URI đó. "

Do đó, tạo hoặc cập nhật dựa trên sự tồn tại của tài nguyên .

Tài liệu tham khảo:


11
Bài đăng này rất hữu ích với tôi khi hiểu rằng POST thêm "một cái gì đó" khi còn nhỏ vào bộ sưu tập đã cho (URI), trong khi PUT định nghĩa rõ ràng "cái gì đó" tại vị trí URI đã cho.
kwah

3
Đây là câu trả lời tốt nhất, ở đây, tôi nghĩ: không ai trong số "POST này có thể cập nhật tài nguyên" vô nghĩa. Tôi thích tuyên bố của bạn, "Cập nhật chỉ có thể được thực hiện với PUT".
Thomas

4
Không, PUT không phải để cập nhật hoặc tạo. Nó là để thay thế. Lưu ý rằng bạn có thể thay thế không có gì bằng hiệu ứng tạo.
thecoshman

2
@ 7hi4g0 PUT là để cập nhật với sự thay thế hoàn toàn, nói cách khác, nó thay thế. Bạn thay thế không có gì bằng một cái gì đó, hoặc một cái gì đó bằng một thứ hoàn toàn mới. PUT không phải để thực hiện một thay đổi nhỏ (trừ khi bạn có khách hàng thực hiện thay đổi nhỏ đó và cung cấp toàn bộ phiên bản mới, ngay cả những gì vẫn giữ nguyên). Đối với sửa đổi một phần, PATCH là phương pháp được lựa chọn.
thecoshman

1
@thecoshman Bạn có thể, nhưng sẽ không quá rõ ràng rằng sáng tạo cũng được đề cập trong đó. Trong trường hợp này, tốt hơn là nên rõ ràng.
7hi4g0

175

POST có nghĩa là "tạo mới" như trong "Đây là đầu vào để tạo người dùng, tạo nó cho tôi".

PUT có nghĩa là "chèn, thay thế nếu đã tồn tại" như trong "Đây là dữ liệu cho người dùng 5".

Bạn POSTđến example.com/users vì bạn chưa biết URLngười dùng, bạn muốn máy chủ tạo nó.

Bạn PUTvào example.com/users/id vì bạn muốn thay thế / tạo một người dùng cụ thể .

Đăng hai lần với cùng một dữ liệu có nghĩa là tạo hai người dùng giống hệt nhau với các id khác nhau. PUT hai lần với cùng một dữ liệu sẽ tạo cho người dùng lần đầu tiên và cập nhật anh ta về cùng trạng thái lần thứ hai (không thay đổi). Vì bạn kết thúc với cùng một trạng thái sau khi PUTbạn thực hiện nó bao nhiêu lần, nó được cho là "mạnh như nhau" mỗi lần - bình thường. Điều này rất hữu ích để tự động thử lại các yêu cầu. Không còn nữa "bạn có chắc chắn muốn gửi lại" khi bạn nhấn nút quay lại trên trình duyệt.

Một lời khuyên chung là sử dụng POSTkhi bạn cần máy chủ kiểm soát việc URLtạo tài nguyên của bạn. Sử dụng PUTkhác. Thích PUT hơn POST.


12
Sự chậm chạp có thể khiến nó thường được dạy rằng chỉ có hai động từ bạn cần: GET và POST. NHẬN để có được, POST để thay đổi. Ngay cả PUT và DELETE cũng được thực hiện bằng POST. Hỏi PUT thực sự có nghĩa là gì 25 năm sau có thể là một dấu hiệu chúng ta đã học sai lúc đầu. Sự phổ biến của REST đã đưa mọi người trở lại những điều cơ bản nơi mà bây giờ chúng ta phải học hỏi những sai lầm tồi tệ. POST đã được sử dụng quá mức và bây giờ thường được dạy không chính xác. Phần hay nhất: "POST hai lần với cùng một dữ liệu có nghĩa là tạo hai [tài nguyên] giống hệt nhau". Điểm tuyệt vời!
maxpolk

1
Làm thế nào bạn có thể sử dụng PUT để tạo bản ghi bằng ID, như trong ví dụ của bạn user 5nếu nó chưa tồn tại? Ý bạn là update, replace if already existssao? hoặc một cái gì đó
Luke

@Coulton: Ý tôi là những gì tôi đã viết. Bạn chèn người dùng 5 nếu bạn PUT cho / users / 5 và # 5 chưa tồn tại.
Alexander Torstling

@Coulton: Và PUTcũng có thể được sử dụng để thay thế toàn bộ giá trị của một tài nguyên hiện có .
DavidRR

1
"Thích PUT hơn POST" ... quan tâm để biện minh cho điều đó?
thecoshman

173

Tôi muốn thêm lời khuyên "thực dụng" của tôi. Sử dụng PUT khi bạn biết "id" mà đối tượng bạn đang lưu có thể được truy xuất. Sử dụng PUT sẽ không hoạt động tốt nếu bạn cần, ví dụ, một id cơ sở dữ liệu được tạo sẽ được trả về để bạn thực hiện tra cứu hoặc cập nhật trong tương lai.

Vì vậy: Để lưu người dùng hiện tại hoặc người dùng nơi khách hàng tạo id và được xác minh rằng id là duy nhất:

PUT /user/12345 HTTP/1.1  <-- create the user providing the id 12345
Host: mydomain.com

GET /user/12345 HTTP/1.1  <-- return that user
Host: mydomain.com

Mặt khác, sử dụng POST để tạo đối tượng ban đầu và PUT để cập nhật đối tượng:

POST /user HTTP/1.1   <--- create the user, server returns 12345
Host: mydomain.com

PUT /user/12345 HTTP/1.1  <--- update the user
Host: mydomain.com

17
Trên thực tế, nó nên được POST /users. (Lưu ý đó /userslà số nhiều.) Điều này có ảnh hưởng đến việc tạo người dùng mới và biến nó thành tài nguyên con của /usersbộ sưu tập.
DavidRR

6
@DavidRR để công bằng, làm thế nào để xử lý các nhóm là một cuộc tranh luận khác hoàn toàn. GET /userscó ý nghĩa, nó đọc như bạn muốn, nhưng tôi sẽ ổn với GET /user/<id>hoặc POST /user(với tải trọng cho người dùng mới đã nói) bởi vì nó đọc chính xác 'lấy cho tôi người dùng 5' là số lẻ, nhưng 'lấy cho tôi người dùng 5' thì tự nhiên hơn. Có lẽ tôi vẫn rơi vào khía cạnh số nhiều mặc dù :)
thecoshman

126

Sử dụng POST để tạo và PUT để cập nhật. Dù sao đó cũng là cách Ruby on Rails làm việc đó.

PUT    /items/1      #=> update
POST   /items        #=> create

4
POST /itemsthêm một mục mới vào một tài nguyên đã được xác định ('mục'). Nó không, như câu trả lời, "tạo một nhóm." Tôi không hiểu tại sao điều này có 12 phiếu.
David J.

Ra khỏi hộp, Rails không hỗ trợ 'tạo nhóm' thông qua REST. Để 'tạo một nhóm' theo ý tôi là 'tạo tài nguyên' bạn phải thực hiện thông qua mã nguồn.
David J.

8
Đây là một hướng dẫn công bằng, nhưng đơn giản hóa. Như các câu trả lời khác đề cập, một trong hai phương pháp có thể được sử dụng cho cả tạo và cập nhật.
Brad Koch

2
Tôi đồng ý với câu trả lời với một sửa đổi nhỏ. Sử dụng POST để tạo và PUT để cập nhật tài nguyên hoàn toàn. Để cập nhật một phần, chúng ta có thể sử dụng PUT hoặc PATCH. Hãy nói rằng chúng tôi muốn cập nhật trạng thái của một nhóm. Chúng tôi có thể sử dụng PUT / nhóm / 1 / trạng thái với trạng thái là tải trọng yêu cầu hoặc VÒI / nhóm / 1 với các chi tiết về hành động trong tải trọng
java_geek

2
Cũng cần phải làm rõ rằng PUT /items/42nó cũng hợp lệ để tạo tài nguyên, nhưng chỉ khi khách hàng có đặc quyền đặt tên tài nguyên . (Rails có cho phép khách hàng đặc quyền đặt tên này không?)
DavidRR 16/12/14

123

Cả hai đều được sử dụng để truyền dữ liệu giữa máy khách đến máy chủ, nhưng có sự khác biệt tinh tế giữa chúng, đó là:

Nhập mô tả hình ảnh ở đây

Sự giống nhau:

  • PUT tức là lấy và đặt nó ở đâu.
  • POST như gửi mail trong bưu điện.

nhập mô tả hình ảnh ở đây

Truyền thông xã hội / Mạng tương tự:

  • Đăng trên phương tiện truyền thông xã hội: khi chúng tôi đăng tin nhắn, nó tạo ra bài viết mới.
  • Đặt (tức là chỉnh sửa) cho tin nhắn chúng tôi đã đăng.

21
@MobileMon Không, phương thức REST không phải là CRUD.
JLR

1
Tôi muốn nói PUT cho UPSERTS
Hola Soy Edu Feliz Navidad

@MobileMon no: POST khi bạn tạo tài nguyên mới và bạn không biết điểm cuối cuối cùng để có được nó. PUT cho các trường hợp khác.
Portekoi

67

REST rất khái niệm cao cấp. Trên thực tế, nó thậm chí không đề cập đến HTTP!

Nếu bạn có bất kỳ nghi ngờ nào về cách triển khai REST trong HTTP, bạn luôn có thể xem Giao thức xuất bản nguyên tử (AtomPub) thông số kỹ thuật của . AtomPub là một tiêu chuẩn để viết các dịch vụ web RESTful với HTTP được phát triển bởi nhiều bộ phát sáng HTTP và REST, với một số đầu vào từ Roy Fielding, nhà phát minh của REST và (đồng phát minh ra HTTP).

Trên thực tế, bạn thậm chí có thể sử dụng trực tiếp AtomPub. Mặc dù nó ra khỏi cộng đồng viết blog, nhưng nó không bị hạn chế trong việc viết blog: đó là một giao thức chung để tương tác RESTly với các bộ sưu tập tài nguyên tùy ý (lồng nhau) thông qua HTTP. Nếu bạn có thể biểu diễn ứng dụng của mình dưới dạng tập hợp các tài nguyên lồng nhau, thì bạn chỉ cần sử dụng AtomPub và không cần lo lắng về việc nên sử dụng PUT hay POST, Mã trạng thái HTTP nào sẽ trả về và tất cả các chi tiết đó.

Đây là những gì AtomPub nói về việc tạo tài nguyên (phần 9.2):

Để thêm thành viên vào Bộ sưu tập, khách hàng gửi yêu cầu POST đến URI của Bộ sưu tập.


8
Không có gì sai khi cho phép PUT tạo tài nguyên. Chỉ cần lưu ý rằng nó có nghĩa là khách hàng cung cấp URL.
Julian Reschke

5
Có điều gì đó rất sai khi cho phép PUT tạo tài nguyên: ứng dụng khách cung cấp URL. Đó là công việc của máy chủ!
Joshcodes

@Joshcodes Không phải lúc nào cũng là công việc của máy chủ để tạo id khách hàng. Tôi đã ngày càng thấy các thiết kế cho phép khách hàng tạo ra một số loại UUID làm id tài nguyên. Thiết kế này cho vay đặc biệt để tăng quy mô.
Justin Ohms

@JustinOhms Tôi đồng ý với quan điểm của bạn về ID khách hàng tạo (lưu ý phụ: tất cả các hệ thống do tôi thiết kế từ khoảng năm 2008 yêu cầu khách hàng tạo ID dưới dạng UUID / Hướng dẫn). Điều đó không có nghĩa là khách hàng nên chỉ định URL.
Joshcodes

1
Có, nếu tài nguyên đã tồn tại, sử dụng PUT. Tuy nhiên, trong hầu hết các trường hợp, các tài nguyên nên được tạo bằng POST và máy khách không được cung cấp URL. Roy Fielding đồng ý với tuyên bố này FWIW: roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
Joshcodes 7/2/2017

61

Quyết định về việc sử dụng PUT hay POST để tạo tài nguyên trên máy chủ có API HTTP + REST dựa trên người sở hữu cấu trúc URL. Để khách hàng biết hoặc tham gia vào việc xác định, cấu trúc URL là một khớp nối không cần thiết gần giống với các khớp nối không mong muốn phát sinh từ SOA. Thoát khỏi các loại khớp nối là lý do REST rất phổ biến. Do đó, phương pháp thích hợp để sử dụng là POST. Có các ngoại lệ cho quy tắc này và chúng xảy ra khi khách hàng muốn duy trì quyền kiểm soát cấu trúc vị trí của các tài nguyên mà nó triển khai. Điều này là hiếm và có khả năng có nghĩa là một cái gì đó khác là sai.

Tại thời điểm này, một số người sẽ lập luận rằng nếu sử dụng URL RESTful , khách hàng sẽ biết URL của tài nguyên và do đó PUT được chấp nhận. Rốt cuộc, đây là lý do tại sao các URL chính thức, bình thường hóa, Ruby on Rails, Django rất quan trọng, hãy nhìn vào API API API blah blah blah. Những người đó cần phải hiểu rằng không có thứ gì gọi là Restful-URL và chính Roy Fielding nói rằng :

API REST không được xác định tên tài nguyên hoặc cấu trúc phân cấp cố định (khớp nối rõ ràng giữa máy khách và máy chủ). Máy chủ phải có quyền tự do kiểm soát không gian tên của chính họ. Thay vào đó, cho phép các máy chủ hướng dẫn khách hàng cách xây dựng các URI phù hợp, chẳng hạn như được thực hiện trong các biểu mẫu HTML và mẫu URI, bằng cách xác định các hướng dẫn đó trong các loại phương tiện và quan hệ liên kết. [Thất bại ở đây ngụ ý rằng các máy khách đang giả định cấu trúc tài nguyên do thông tin ngoài băng, chẳng hạn như tiêu chuẩn dành riêng cho miền, là hướng dữ liệu tương đương với khớp nối chức năng của RPC].

http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

Ý tưởng về RESTful-URL thực sự là vi phạm REST vì máy chủ chịu trách nhiệm về cấu trúc URL và được tự do quyết định cách sử dụng nó để tránh ghép nối. Nếu điều này làm bạn bối rối, bạn sẽ đọc về tầm quan trọng của việc tự khám phá về thiết kế API.

Sử dụng POST để tạo tài nguyên đi kèm với việc xem xét thiết kế vì POST không phải là idempotent. Điều này có nghĩa là việc lặp lại POST nhiều lần không đảm bảo cùng một hành vi mỗi lần. Điều này khiến mọi người sợ sử dụng PUT để tạo tài nguyên khi họ không nên.Họ biết điều đó sai (POST là dành cho TẠO) nhưng dù sao họ cũng làm điều đó vì họ không biết cách giải quyết vấn đề này. Mối quan tâm này được thể hiện trong tình huống sau:

  1. Máy khách POST một tài nguyên mới cho máy chủ.
  2. Máy chủ xử lý yêu cầu và gửi phản hồi.
  3. Khách hàng không bao giờ nhận được phản hồi.
  4. Máy chủ không biết khách hàng chưa nhận được phản hồi.
  5. Máy khách không có URL cho tài nguyên (do đó PUT không phải là một tùy chọn) và lặp lại POST.
  6. POST không phải là idempotent và máy chủ

Bước 6 là nơi mọi người thường bối rối về những việc cần làm. Tuy nhiên, không có lý do gì để tạo ra một loại bùn để giải quyết vấn đề này. Thay vào đó, HTTP có thể được sử dụng như được chỉ định trong RFC 2616 và máy chủ trả lời:

10,4.10 409 Xung đột

Yêu cầu không thể được hoàn thành do xung đột với trạng thái hiện tại của tài nguyên. Mã này chỉ được phép trong các tình huống mà người dùng dự kiến ​​có thể giải quyết xung đột và gửi lại yêu cầu. Cơ quan phản hồi NÊN bao gồm đủ

thông tin để người dùng nhận ra nguồn gốc của xung đột. Lý tưởng nhất, thực thể phản hồi sẽ bao gồm đủ thông tin cho người dùng hoặc tác nhân người dùng để khắc phục sự cố; tuy nhiên, điều đó có thể là không thể và không bắt buộc.

Xung đột rất có thể xảy ra để đáp ứng yêu cầu PUT. Ví dụ: nếu phiên bản đang được sử dụng và thực thể là PUT bao gồm các thay đổi đối với tài nguyên xung đột với tài nguyên được tạo bởi yêu cầu trước đó (bên thứ ba), máy chủ có thể sử dụng phản hồi 409 để cho biết rằng nó không thể hoàn thành yêu cầu . Trong trường hợp này, thực thể phản hồi có thể sẽ chứa một danh sách các khác biệt giữa hai phiên bản theo định dạng được xác định bởi Kiểu nội dung phản hồi.

Trả lời với mã trạng thái 409 Xung đột là cách truy đòi đúng vì :

  • Việc thực hiện POST dữ liệu có ID phù hợp với tài nguyên đã có trong hệ thống là một cuộc xung đột với trạng thái hiện tại của tài nguyên.
  • Vì phần quan trọng là để khách hàng hiểu máy chủ có tài nguyên và thực hiện hành động thích hợp. Đây là một tình huống của người dùng, nơi người dùng dự kiến ​​có thể giải quyết xung đột và gửi lại yêu cầu.
  • Phản hồi có chứa URL của tài nguyên với ID xung đột và các điều kiện tiên quyết thích hợp cho tài nguyên sẽ cung cấp đủ thông tin cho người dùng hoặc tác nhân người dùng để khắc phục sự cố, đây là trường hợp lý tưởng cho RFC 2616.

Cập nhật dựa trên việc phát hành RFC 7231 thành Thay thế 2616

RFC 7231 được thiết kế để thay thế 2616 và trong Phần 4.3.3 mô tả phản ứng có thể có sau cho POST

Nếu kết quả xử lý POST sẽ tương đương với đại diện của tài nguyên hiện có, máy chủ gốc có thể chuyển hướng tác nhân người dùng đến tài nguyên đó bằng cách gửi phản hồi 303 (Xem Khác) với mã định danh của tài nguyên hiện có trong trường Vị trí. Điều này có lợi ích là cung cấp cho tác nhân người dùng một mã định danh tài nguyên và chuyển biểu diễn thông qua một phương thức dễ sử dụng hơn vào bộ nhớ đệm được chia sẻ, mặc dù phải trả giá cho một yêu cầu bổ sung nếu tác nhân người dùng chưa có bộ đệm đại diện.

Bây giờ có thể chỉ đơn giản là trả về một số 303 trong trường hợp POST được lặp lại. Tuy nhiên, điều ngược lại là đúng. Trả lại một chiếc 303 sẽ chỉ có ý nghĩa nếu nhiều yêu cầu tạo (tạo các tài nguyên khác nhau) trả về cùng một nội dung. Một ví dụ sẽ là "cảm ơn bạn đã gửi tin nhắn yêu cầu của bạn" mà khách hàng không cần tải lại mỗi lần. RFC 7231 vẫn duy trì trong mục 4.2.2 rằng POST không phải là idempotent và tiếp tục duy trì POST nên được sử dụng để tạo.

Để biết thêm thông tin về điều này, đọc bài viết này .


Phản hồi Xung đột 409 có phải là mã thích hợp cho việc gì đó như cố gắng tạo tài khoản mới với tên người dùng đã tồn tại không? Tôi đã sử dụng 409 cho các xung đột phiên bản cụ thể, nhưng sau khi đọc câu trả lời của bạn, tôi tự hỏi liệu nó có nên được sử dụng cho bất kỳ yêu cầu "trùng lặp" nào không.
Eric B.

@EricB. Có, trong tình huống bạn mô tả "do xung đột với trạng thái hiện tại của tài nguyên", thao tác không thành công. Ngoài ra, thật hợp lý khi hy vọng rằng người dùng có thể giải quyết xung đột và nội dung thư chỉ cần thông báo cho người dùng rằng tên người dùng đã tồn tại.
Joshcodes

@Joshcodes bạn có thể nói thêm về quá trình giải quyết xung đột? Trong trường hợp này, nếu tên người dùng đã tồn tại thì khách hàng có thể nhắc người dùng cuối cho một tên người dùng khác không? Điều gì xảy ra nếu khách hàng thực sự đang cố gắng sử dụng POST để thay đổi tên người dùng? Các yêu cầu PUT vẫn nên được sử dụng để cập nhật các tham số, trong khi POST được sử dụng để tạo các đối tượng cho dù đó là một hoặc nhiều lần? Cảm ơn.
BFar

@ BFar2 nếu tên người dùng đã tồn tại thì khách hàng sẽ nhắc người dùng. Để thay đổi tên người dùng, giả sử tên người dùng là một phần của tài nguyên đã được tạo cần sửa đổi, PUT sẽ được sử dụng vì bạn đúng, POST được sử dụng để tạo, luôn luôn và PUT để cập nhật.
Joshcodes 23/1/2015

giải thích mọi thứ bằng ngôn ngữ ngắn và hiệu quả cũng là một kỹ năng đáng mong đợi
Junchen Liu

53

Tôi thích lời khuyên này, từ định nghĩa PUT của RFC 2616 :

Sự khác biệt cơ bản giữa các yêu cầu POST và PUT được phản ánh theo nghĩa khác nhau của URI yêu cầu. URI trong yêu cầu POST xác định tài nguyên sẽ xử lý thực thể kèm theo. Tài nguyên đó có thể là một quá trình chấp nhận dữ liệu, một cổng vào một số giao thức khác hoặc một thực thể riêng biệt chấp nhận các chú thích. Ngược lại, URI trong yêu cầu PUT xác định thực thể kèm theo yêu cầu - tác nhân người dùng biết URI được dự định là gì và máy chủ KHÔNG cố gắng áp dụng yêu cầu cho một số tài nguyên khác.

Điều này phù hợp với lời khuyên khác ở đây, rằng PUT được áp dụng tốt nhất cho các tài nguyên đã có tên và POST rất tốt để tạo một đối tượng mới theo tài nguyên hiện có (và cho phép máy chủ đặt tên cho nó).

Tôi giải thích điều này và các yêu cầu về tính không thay đổi trên PUT, có nghĩa là:

  • POST tốt cho việc tạo các đối tượng mới trong một bộ sưu tập (và việc tạo không cần phải là idempotent)
  • PUT tốt cho việc cập nhật các đối tượng hiện có (và cập nhật cần phải bình thường)
  • POST cũng có thể được sử dụng để cập nhật không bình thường cho các đối tượng hiện có (đặc biệt là thay đổi một phần của đối tượng mà không chỉ định toàn bộ - nếu bạn nghĩ về nó, việc tạo thành viên mới của bộ sưu tập thực sự là một trường hợp đặc biệt của loại này cập nhật, từ quan điểm của bộ sưu tập)
  • PUT cũng có thể được sử dụng để tạo khi và chỉ khi bạn cho phép máy khách đặt tên tài nguyên. Nhưng vì các máy khách REST không cần phải đưa ra các giả định về cấu trúc URL, nên điều này ít hơn theo tinh thần dự định của mọi thứ.

3
"POST cũng có thể được sử dụng để cập nhật phi idempotent đến đối tượng hiện có (đặc biệt, thay đổi một phần của một đối tượng mà không chỉ định toàn bộ sự việc" Đó là những gì PATCH là cho
Snuggs

48

Nói ngắn gọn:

PUT là idempotent, trong đó trạng thái tài nguyên sẽ giống nhau nếu cùng một hoạt động được thực hiện một lần hoặc nhiều lần.

POST không phải là idempotent, trong đó trạng thái tài nguyên có thể trở nên khác biệt nếu hoạt động được thực thi nhiều lần so với thực hiện một lần duy nhất.

Tương tự với truy vấn cơ sở dữ liệu

PUT Bạn có thể nghĩ tương tự như "CẬP NHẬT STUDENT SET address =" abc "trong đó id =" 123 ";

BÀI VIẾT Bạn có thể nghĩ về một cái gì đó như "XÁC NHẬN VÀO SINH VIÊN (tên, địa chỉ) GIÁ TRỊ (" abc "," xyzzz ");

Id sinh viên được tạo tự động.

Với PUT, nếu cùng một truy vấn được thực thi nhiều lần hoặc một lần, trạng thái bảng STUDENT vẫn giữ nguyên.

Trong trường hợp POST, nếu cùng một truy vấn được thực thi nhiều lần thì nhiều bản ghi Sinh viên sẽ được tạo trong cơ sở dữ liệu và trạng thái cơ sở dữ liệu thay đổi trên mỗi lần thực hiện truy vấn "INSERT".

LƯU Ý: PUT cần một vị trí tài nguyên (đã là tài nguyên) mà cập nhật cần phải xảy ra, trong khi POST không yêu cầu điều đó. Do đó, POST trực quan có nghĩa là để tạo ra một tài nguyên mới, trong khi PUT là cần thiết để cập nhật tài nguyên đã có.

Một số có thể đi kèm với các cập nhật có thể được thực hiện với POST. Không có quy tắc cứng nào nên sử dụng để cập nhật hoặc sử dụng quy tắc nào để tạo. Một lần nữa, đây là những quy ước, và theo trực giác, tôi nghiêng về lý luận đã đề cập ở trên và làm theo nó.


6
đối với PUT tương tự như truy vấn INSERT hoặc UPDATE
Eugen Konkov

1
thực sự PUT Bạn có thể nghĩ tương tự như "UPDATE STUDENT SET address =" abc "trong đó id =" 123 "; sẽ là một câu lệnh cho PATCH." UPDATE STUDENT SET address = "abc", name = "newname" where id = " 123 "sẽ là một sự tương tự chính xác cho PUT
mko

Đặt cũng có thể được sử dụng cho INSERT. Ví dụ: nếu máy chủ của bạn phát hiện ra bạn đang cố tải lên cùng một tệp nhiều lần, nó sẽ khiến yêu cầu của bạn trở nên bình thường. (Không có tệp tải lên mới được thực hiện).
kiwicomb123

43

POST giống như đăng một bức thư vào hộp thư hoặc gửi email đến hàng đợi email. PUT giống như khi bạn đặt một vật thể vào lỗ hình khối hoặc một vị trí trên giá (nó có một địa chỉ đã biết).

Với POST, bạn đang đăng lên địa chỉ của QUEUE hoặc THU. Với PUT, bạn đang đặt địa chỉ của ITEM.

PUT là idempotent. Bạn có thể gửi yêu cầu 100 lần và nó sẽ không thành vấn đề. POST không phải là idempotent. Nếu bạn gửi yêu cầu 100 lần, bạn sẽ nhận được 100 email hoặc 100 chữ cái trong hộp thư của mình.

Một quy tắc chung: nếu bạn biết id hoặc tên của mặt hàng, hãy sử dụng PUT. Nếu bạn muốn id hoặc tên của mặt hàng được chỉ định bởi bên nhận, hãy sử dụng POST.

POST so với PUT


1
Không, PUT ngụ ý rằng bạn biết URL. Nếu bạn chỉ biết ID thì POST với ID đó để lấy URL.
Joshcodes

6
Id là một phần của URL, vì vậy, hãy sử dụng PUT nếu bạn biết URL (bao gồm id).
Homer6

Không, URL được xác định bởi máy chủ và ID không nhất thiết phải là một phần của URL. Roy Fielding sẽ nói với bạn như vậy hoặc bạn chỉ có thể đọc luận án của mình .
Joshcodes

@Joshcodes, đó có phải là giả sử REST không? Trong kiến ​​trúc RESTful, id mục chắc chắn là một phần của URL, như trong: / people / 123. Tôi thích trang web này cho REST: microformats.org/wiki/rest/urls
Beez 26/12/13

1
@Beez liên kết mircoformats gợi ý một cách tốt để các máy chủ cấu trúc URL của họ nhưng máy chủ xác định URL. Các khách hàng bên cạnh không bao giờ làm. Xem câu trả lời của tôi hoặc bài viết liên quan nếu bạn không hiểu điều này.
Mã vạch

39

Câu trả lời mới (bây giờ tôi hiểu REST tốt hơn):

PUT chỉ đơn thuần là một tuyên bố về nội dung mà dịch vụ nên sử dụng, từ bây giờ, sử dụng để hiển thị các đại diện của tài nguyên được xác định bởi máy khách; POST là một tuyên bố về nội dung mà dịch vụ nên, kể từ bây giờ, chứa (có thể trùng lặp) nhưng tùy thuộc vào máy chủ cách xác định nội dung đó.

PUT x(nếu xxác định tài nguyên ): "Thay thế nội dung của tài nguyên được xác định bằng xnội dung của tôi."

PUT x(nếu xkhông xác định tài nguyên): "Tạo tài nguyên mới chứa nội dung của tôi và sử dụng xđể xác định tài nguyên đó."

POST x: "Lưu trữ nội dung của tôi và cung cấp cho tôi một mã định danh mà tôi có thể sử dụng để xác định tài nguyên (cũ hoặc mới) có chứa nội dung đã nói (có thể trộn lẫn với nội dung khác). Tài nguyên đã nói phải giống hệt hoặc phụ thuộc vào tài nguyên xnhận dạng." Thông thường " tài nguyên của y phụ thuộc vào tài nguyên của x " nhưng không nhất thiết phải được thực hiện bằng cách biến y thành một đường dẫn phụ của x (ví dụ x = /fooy = /foo/bar) và sửa đổi (các) biểu diễn của tài nguyên x để phản ánh sự tồn tại của một tài nguyên mới, ví dụ như với một siêu liên kết đến yTài nguyên và một số siêu dữ liệu. Chỉ có cái sau mới thực sự cần thiết cho thiết kế tốt, vì URL mờ trong REST - bạn phải sử dụng hypermedia thay vì xây dựng URL phía máy khách để đi qua dịch vụ.

Trong REST, không có thứ gọi là tài nguyên chứa "nội dung". Tôi gọi là "nội dung" cho dữ liệu mà dịch vụ sử dụng để hiển thị các biểu diễn một cách nhất quán. Nó thường bao gồm một số hàng liên quan trong cơ sở dữ liệu hoặc tệp (ví dụ: tệp hình ảnh). Tùy thuộc vào dịch vụ để chuyển đổi nội dung của người dùng thành thứ mà dịch vụ có thể sử dụng, ví dụ: chuyển đổi tải trọng JSON thành các câu lệnh SQL.

Câu trả lời gốc (có thể dễ đọc hơn) :

PUT /something(nếu /somethingđã tồn tại): "Lấy bất cứ thứ gì bạn có /somethingvà thay thế nó bằng những gì tôi đưa cho bạn."

PUT /something(nếu /somethingchưa tồn tại): "Lấy những gì tôi đưa cho bạn và đặt nó vào /something."

POST /something: "Lấy những gì tôi đưa cho bạn và đặt nó ở bất cứ đâu bạn muốn /somethingmiễn là bạn đưa cho tôi URL của nó khi bạn hoàn thành."


Nhưng làm thế nào bạn có thể sử dụng PUT để tạo tài nguyên mới nếu nó không tồn tại, trong khi phương thức tạo ID của bạn là Tự động tăng? Thông thường ORM tự động tạo ID cho bạn, như cách bạn muốn nó ở trong POST chẳng hạn. Điều đó có nghĩa là nếu bạn muốn triển khai PUT đúng cách, bạn phải thay đổi thế hệ tự động id? Điều này thật bất tiện nếu câu trả lời là có.
Roni Axelrad

1
@RoniAxelrad: PUT giống như một câu lệnh "XÁC NHẬN HOẶC CẬP NHẬT" trong đó bạn bao gồm khóa trong câu lệnh, vì vậy chỉ áp dụng khi bạn không thể va chạm. ví dụ. tên miền của bạn có 'khóa tự nhiên' hoặc bạn sử dụng hướng dẫn. POST giống như chèn vào bảng có phím tăng tự động. Bạn phải được cơ sở dữ liệu cho biết ID nhận được sau khi được chèn. Lưu ý "CHERTN HOẶC CẬP NHẬT" của bạn sẽ thay thế bất kỳ dữ liệu nào trước đó nếu nó tồn tại.
Nigel Thorne

@NigelThorne Cảm ơn câu trả lời của bạn. Vì vậy, ví dụ nếu tôi đang cố gắng PUT một cuốn sách id 10 bằng URI: PUT book / 10. Nếu sách id 10 không tồn tại, tôi nên tạo một cuốn sách có id 10 phải không? nhưng tôi không thể kiểm soát tử số ID tạo, vì nó tự động tăng. Tôi nên làm gì trong tình huống đó?
Roni Axelrad

1
@RoniAxelrad REST PUT tới một ID không tồn tại là một yêu cầu đến máy chủ để tạo tài nguyên. Nó vẫn tùy thuộc vào máy chủ để quyết định nếu nó muốn cho phép điều đó. Máy chủ phụ trách. Nó có thể trả lời với "Không. Tôi sẽ không làm điều đó". Bạn đã làm điều đó nếu người dùng không có đủ quyền ... vv. Máy chủ nói "Không" là được. REST là một quy ước cho phép chúng tôi xác định ý nghĩa của các loại yêu cầu khác nhau ... máy chủ của bạn quyết định phải làm gì với những yêu cầu đó dựa trên logic kinh doanh của bạn :) Ngay cả khi nó nói "không" thì nó vẫn theo REST :)
Nigel Thorne

38

Câu trả lời ngắn:

Quy tắc đơn giản: Sử dụng POST để tạo, sử dụng PUT để cập nhật.

Câu trả lời dài:

BÀI ĐĂNG:

  • POST được sử dụng để gửi dữ liệu đến máy chủ.
  • Hữu ích khi không xác định được URL của tài nguyên

ĐẶT:

  • PUT được sử dụng để chuyển trạng thái đến máy chủ
  • Hữu ích khi biết URL của tài nguyên

Trả lời dài hơn:

Để hiểu nó, chúng ta cần đặt câu hỏi tại sao PUT lại được yêu cầu, những vấn đề mà PUT đang cố gắng giải quyết là POST không thể.

Từ quan điểm của kiến ​​trúc REST, không có vấn đề gì. Chúng tôi có thể sống mà không có PUT. Nhưng theo quan điểm của một nhà phát triển khách hàng, nó làm cho cuộc sống của anh ấy / cô ấy đơn giản hơn rất nhiều.

Trước PUT, khách hàng không thể trực tiếp biết URL mà máy chủ đã tạo hoặc nếu tất cả đã tạo bất kỳ hoặc liệu dữ liệu được gửi đến máy chủ đã được cập nhật hay chưa. PUT làm giảm các nhà phát triển của tất cả những đau đầu. PUT là idempotent, PUT xử lý các điều kiện cuộc đua và PUT cho phép khách hàng chọn URL.


3
Câu trả lời ngắn gọn của bạn có thể RẤT sai. HTTP PUT miễn phí được lặp lại bởi các proxy HTTP. Và vì vậy, nếu PUT thực sự thực hiện SQL INSERT thì nó có thể thất bại lần thứ hai, điều đó có nghĩa là nó sẽ trả về kết quả khác và do đó, nó sẽ không phải là IDEMPOTENT (đó là sự khác biệt giữa PUT và POST)
Kamil Tomšík

36

Ruby on Rails 4.0 sẽ sử dụng phương thức 'PATCH' thay vì PUT để cập nhật một phần.

RFC 5789 nói về PATCH (từ năm 1995):

Một phương pháp mới là cần thiết để cải thiện khả năng tương tác và ngăn ngừa lỗi. Phương thức PUT đã được xác định để ghi đè lên tài nguyên với phần thân hoàn toàn mới và không thể được sử dụng lại để thực hiện các thay đổi một phần. Mặt khác, proxy và bộ nhớ cache, và thậm chí cả máy khách và máy chủ, có thể bị nhầm lẫn về kết quả của hoạt động. POST đã được sử dụng nhưng không có khả năng tương tác rộng (đối với một, không có cách tiêu chuẩn nào để khám phá hỗ trợ định dạng bản vá). PATCH đã được đề cập trong các thông số kỹ thuật HTTP trước đó, nhưng không được xác định hoàn toàn.

" Edge Rails: PATCH là phương thức HTTP chính mới để cập nhật " giải thích nó.


27

Có nguy cơ khôi phục lại những gì đã được nói, có vẻ quan trọng cần nhớ rằng PUT ngụ ý rằng máy khách kiểm soát những gì URL sẽ kết thúc khi tạo tài nguyên. Vì vậy, một phần của sự lựa chọn giữa PUTPOST sẽ là về mức độ bạn có thể tin tưởng khách hàng để cung cấp URL chính xác, chuẩn hóa phù hợp với bất kỳ lược đồ URL nào của bạn.

Khi bạn không thể hoàn toàn tin tưởng khách hàng thực hiện đúng, sẽ phù hợp hơn khi sử dụng POST để tạo một mục mới và sau đó gửi URL lại cho khách hàng trong phản hồi.


2
Tôi hơi muộn với điều này - nhưng ai đó nói điều gì đó tương tự trên một trang web khác đã khiến nó nhấp vào cho tôi. Nếu bạn đang tạo tài nguyên và sử dụng ID tăng tự động làm "định danh" thay vì tên do người dùng gán, thì đó phải là POST.
Ixmatus

2
Điều này không hoàn toàn đúng - PUT vẫn có thể tạo tài nguyên bằng cách tham chiếu nó với tên không chính tắc, miễn là trong phản hồi, máy chủ trả về một Locationtiêu đề chứa tên tài nguyên chính tắc.
Ether

1
@Joshcodes đừng quên rằng bạn có thể có nhiều URI tham chiếu cùng một tài nguyên cơ bản. Vì vậy, những gì Ether nói là lời khuyên âm thanh, khách hàng có thể PUT tới một URL (có thể là ngữ nghĩa hơn, giống như PUT /X-files/series/4/episodes/max) và máy chủ trả lời bằng một URI cung cấp một liên kết duy nhất chính tắc ngắn cho tài nguyên mới đó (ví dụ /X-Ffiles/episodes/91)
thecoshman

@thecoshman vấn đề là mối quan tâm đối với cấu trúc URL không thuộc về khách hàng. Đọc về khám phá bản thân (cũng là một phần của REST) ​​có thể giúp làm rõ điều này.
Joshcodes

@Joshcodes sau đó theo logic đó, khách hàng không bao giờ nên sử dụng PUT để tạo vì họ không nên quan tâm đến việc cung cấp URL. Chà ... trừ khi máy chủ cung cấp URL cho PUT nếu khách hàng muốn đặt nó ... một cái gì đó như "PUT / bình luận / mới" và máy chủ có thể trả lời "204 / bình luận / 234532" nhưng điều đó có vẻ hơi RPC với tôi, khách hàng chỉ nên ĐĂNG / nhận xét ...
thecoshman

24

Nói một cách rất đơn giản, tôi lấy ví dụ về dòng thời gian của Facebook.

Trường hợp 1: Khi bạn đăng một cái gì đó lên dòng thời gian của mình, đó là một mục mới. Vì vậy, trong trường hợp này, họ sử dụng phương thức POST vì phương thức POST không phải là idempotent.

Trường hợp 2: Nếu bạn của bạn nhận xét về bài đăng của bạn lần đầu tiên, điều đó cũng sẽ tạo ra một mục mới trong cơ sở dữ liệu để phương thức POST được sử dụng.

Trường hợp 3: Nếu bạn của bạn chỉnh sửa nhận xét của mình, trong trường hợp này, họ đã có id nhận xét, vì vậy họ sẽ cập nhật nhận xét hiện có thay vì tạo một mục mới trong cơ sở dữ liệu. Do đó, đối với loại hoạt động này, hãy sử dụng phương thức PUT vì nó là idempotent. *

Trong một dòng duy nhất, sử dụng POST để thêm một mục mới trong cơ sở dữ liệu và PUT để cập nhật một cái gì đó trong cơ sở dữ liệu.


4
Nếu bình luận là một đối tượng có thuộc tính như id người dùng, ngày tạo, tin nhắn bình luận, v.v. và tại thời điểm chỉnh sửa, chỉ có thông báo bình luận đang được cập nhật, nên làm gì ở đây?
Habeeb Perwad

PUT được FB sử dụng để cập nhật nhận xét vì tài nguyên hiện có đang được cập nhật và đó là những gì PUT làm (cập nhật tài nguyên). PUT xảy ra là idempotent, trái ngược với POST. Một động từ HTTP là idempotent ảnh hưởng đến việc xử lý lỗi nhưng không ra lệnh sử dụng. Xem câu trả lời của tôi để được giải thích chi tiết hơn: stackoverflow.com/questions/630453/put-vs-post-in-rest/
trộm

21

Việc xem xét quan trọng nhất là độ tin cậy . Nếu một tin nhắn POST bị mất, trạng thái của hệ thống không được xác định. Tự động phục hồi là không thể. Đối với các thông báo PUT, trạng thái chỉ được xác định cho đến khi thử lại thành công đầu tiên.

Ví dụ, có thể không phải là một ý tưởng tốt để tạo giao dịch thẻ tín dụng với POST.

Nếu bạn tình cờ có URI tự động tạo trên tài nguyên của mình, bạn vẫn có thể sử dụng PUT bằng cách chuyển URI được tạo (trỏ đến tài nguyên trống) cho máy khách.

Một số cân nhắc khác:

  • POST không hợp lệ các bản sao được lưu trong bộ nhớ cache của toàn bộ tài nguyên chứa (tính nhất quán tốt hơn)
  • Phản hồi PUT không được lưu trong bộ nhớ cache trong khi các phản hồi POST là (Yêu cầu vị trí nội dung và hết hạn)
  • PUT ít được hỗ trợ bởi ví dụ Java ME, các trình duyệt cũ hơn, tường lửa

Điều này là không chính xác. Đối với POST, trạng thái cũng không được xác định chỉ cho đến lần thử lại thành công đầu tiên. Sau đó, máy chủ chấp nhận POST (tin nhắn không bao giờ đến), ném xung đột 409 cho ID trùng lặp (tin nhắn đến, phản hồi bị mất) hoặc bất kỳ phản hồi hợp lệ nào khác.
Joshcodes

Nói chung, một người dùng sẽ không thể thử lại một cách an toàn thao tác POST vì hoạt động POST không đảm bảo rằng hai thao tác sẽ có cùng hiệu quả như một. Thuật ngữ "ID" không liên quan gì đến HTTP. URI xác định tài nguyên.
Hans Malherbe

Người dùng có thể "an toàn" thử lại thao tác POST bao nhiêu lần tùy ý. Nó sẽ chỉ nhận được một lỗi ID trùng lặp (giả sử tài nguyên có ID) hoặc lỗi dữ liệu trùng lặp (giả sử đó là một vấn đề và tài nguyên không có ID).
Joshcodes

Bangs tựa đầu vào tường. HTTP không có giải pháp cho vấn đề về độ tin cậy và điều này không được hiểu rõ, không được thảo luận nhiều và đơn giản là không phục vụ cho phần lớn các ứng dụng web. @Joshcodes Tôi có câu trả lời cho câu hỏi này. Tôi cơ bản đồng ý với Hans. Có vấn đề.
bbsimonbb

@bbsimonbb, HTTP có một bộ phản hồi lỗi mạnh mẽ và được ghi lại rõ ràng. Câu trả lời của tôi cho câu hỏi này ( stackoverflow.com/questions/630453/put-vs-post-in-rest/ mẹo ) bao gồm cách sử dụng http theo đặc điểm kỹ thuật để đạt được sự thống nhất.
Joshcodes

17

Những độc giả mới tham gia chủ đề này sẽ bị ấn tượng bởi cuộc thảo luận bất tận về những gì bạn nên làm và sự vắng mặt tương đối của các bài học từ kinh nghiệm. Thực tế là REST được "ưu tiên" hơn SOAP, tôi cho rằng, việc học hỏi kinh nghiệm ở cấp độ cao, nhưng lòng tốt chúng ta có phải đã tiến bộ từ đó không? Đó là năm 2016. Luận án của Roy là vào năm 2000. Chúng ta đã phát triển những gì? Có vui không? Có dễ dàng để tích hợp với? Hỗ trợ? Nó sẽ xử lý sự gia tăng của điện thoại thông minh và kết nối di động không ổn định?

Theo ME, mạng thực tế là không đáng tin cậy. Yêu cầu thời gian chờ. Các kết nối được thiết lập lại. Mạng đi xuống hàng giờ hoặc nhiều ngày tại một thời điểm. Xe lửa đi vào đường hầm với người dùng di động trên tàu. Đối với bất kỳ yêu cầu nào (như thỉnh thoảng được thừa nhận trong tất cả các cuộc thảo luận này), yêu cầu có thể rơi xuống nước trên đường đi hoặc phản hồi có thể rơi xuống nước trên đường trở về. Trong các điều kiện này, việc đưa ra các yêu cầu PUT, POST và DELETE trực tiếp chống lại các tài nguyên thực sự luôn khiến tôi cảm thấy hơi tàn bạo và ngây thơ.

HTTP không làm gì để đảm bảo hoàn thành đáng tin cậy cho phản hồi yêu cầu và điều đó thật tốt vì đây là công việc của các ứng dụng nhận biết mạng. Phát triển một ứng dụng như vậy, bạn có thể chuyển qua các vòng để sử dụng PUT thay vì POST, sau đó nhiều vòng hơn để đưa ra một loại lỗi nhất định trên máy chủ nếu bạn phát hiện các yêu cầu trùng lặp. Quay lại máy khách, sau đó bạn phải nhảy qua các vòng để diễn giải các lỗi này, tải lại, xác nhận lại và đăng lại.

Hoặc bạn có thể làm điều này : coi các yêu cầu không an toàn của bạn là tài nguyên người dùng đơn lẻ phù hợp (hãy gọi chúng là hành động). Khách hàng yêu cầu một "hành động" mới trên một tài nguyên thực sự với một POST trống cho tài nguyên. POST sẽ chỉ được sử dụng cho việc này. Khi đã sở hữu URI một cách an toàn cho hành động mới được tạo ra, khách hàng PUT yêu cầu không an toàn đối với URI hành động, chứ không phải tài nguyên đích . Giải quyết hành động và cập nhật tài nguyên "thực" là công việc chính xác của API của bạn và ở đây được tách ra khỏi mạng không đáng tin cậy.

Máy chủ thực hiện công việc, trả về phản hồi và lưu trữ theo URI hành động đã thỏa thuận . Nếu có lỗi xảy ra, máy khách sẽ lặp lại yêu cầu (hành vi tự nhiên!) Và nếu máy chủ đã nhìn thấy nó, nó sẽ lặp lại phản hồi được lưu trữ và không làm gì khác .

Bạn sẽ nhanh chóng phát hiện ra sự tương đồng với những lời hứa: chúng tôi tạo và trả lại chỗ giữ chỗ cho kết quả trước khi làm bất cứ điều gì. Cũng giống như một lời hứa, một hành động có thể thành công hoặc thất bại một lần, nhưng kết quả của nó có thể được tìm nạp nhiều lần.

Trên hết, chúng tôi cho phép gửi và nhận ứng dụng một cơ hội để liên kết hành động được xác định duy nhất với tính duy nhất trong môi trường tương ứng của chúng. Và chúng tôi có thể bắt đầu yêu cầu và thực thi!, Hành vi có trách nhiệm từ khách hàng: lặp lại yêu cầu của bạn bao nhiêu tùy thích, nhưng đừng tạo ra một hành động mới cho đến khi bạn sở hữu kết quả dứt khoát từ kết quả hiện có.

Như vậy, nhiều vấn đề gai góc biến mất. Các yêu cầu chèn lặp đi lặp lại sẽ không tạo ra các bản sao và chúng tôi không tạo tài nguyên thực sự cho đến khi chúng tôi sở hữu dữ liệu. (các cột cơ sở dữ liệu có thể ở mức không rỗng). Yêu cầu cập nhật lặp lại sẽ không đạt trạng thái không tương thích và sẽ không ghi đè các thay đổi tiếp theo. Khách hàng có thể (tải lại) và xử lý liền mạch xác nhận ban đầu vì bất kỳ lý do gì (máy khách bị lỗi, phản hồi bị mất, v.v.).

Các yêu cầu xóa liên tiếp có thể xem và xử lý xác nhận ban đầu mà không gặp lỗi 404. Nếu mọi thứ mất nhiều thời gian hơn dự kiến, chúng tôi có thể trả lời tạm thời và chúng tôi có một nơi mà khách hàng có thể kiểm tra lại kết quả cuối cùng. Phần đẹp nhất của mẫu này là thuộc tính Kung-Fu (Panda). Chúng tôi có một điểm yếu, xu hướng để khách hàng lặp lại yêu cầu bất cứ khi nào họ không hiểu phản hồi và biến nó thành một điểm mạnh :-)

Trước khi nói với tôi đây không phải là RESTful, vui lòng xem xét nhiều cách mà các nguyên tắc REST được tôn trọng. Khách hàng không xây dựng URL. API vẫn có thể khám phá, mặc dù có một chút thay đổi về ngữ nghĩa. Động từ HTTP được sử dụng một cách thích hợp. Nếu bạn nghĩ rằng đây là một thay đổi lớn để thực hiện, tôi có thể nói với bạn từ kinh nghiệm rằng nó không phải.

Nếu bạn nghĩ rằng bạn sẽ có một lượng dữ liệu khổng lồ để lưu trữ, hãy nói về khối lượng: một xác nhận cập nhật điển hình là một phần của một kilobyte. HTTP hiện cung cấp cho bạn một hoặc hai phút để trả lời dứt khoát. Ngay cả khi bạn chỉ lưu trữ các hành động trong một tuần, khách hàng vẫn có nhiều cơ hội để bắt kịp. Nếu bạn có âm lượng rất lớn, bạn có thể muốn lưu trữ giá trị khóa tuân thủ axit chuyên dụng hoặc giải pháp trong bộ nhớ.


1
Không lưu trữ phản ứng giống như duy trì một phiên? Điều này sẽ gây ra vấn đề mở rộng (ngang).
Saurabh Harwande

17

Ngoài những khác biệt được đề xuất bởi những người khác, tôi muốn thêm một.

Trong phương thức POST, bạn có thể gửi thông số cơ thể trongform-data

Trong phương thức PUT, bạn phải gửi thông số cơ thể trongx-www-form-urlencoded

Tiêu đề Content-Type:application/x-www-form-urlencoded

Theo đó, bạn không thể gửi tệp hoặc dữ liệu nhiều phần trong phương thức PUT

BIÊN TẬP

Loại nội dung "application / x-www-form-urlencoding" không hiệu quả để gửi số lượng lớn dữ liệu nhị phân hoặc văn bản có chứa các ký tự không phải ASCII. Loại nội dung "nhiều dữ liệu / biểu mẫu dữ liệu" nên được sử dụng để gửi biểu mẫu có chứa tệp, dữ liệu không phải ASCII và dữ liệu nhị phân.

Có nghĩa là nếu bạn phải nộp

các tệp, dữ liệu không phải ASCII và dữ liệu nhị phân

bạn nên sử dụng phương thức POST


3
Tại sao điều này không được nêu lên? Nếu đúng, đây là một sự phân biệt quan trọng phải không?
Iofacture

2
Tôi đã đối mặt với nó khi triển khai API cho cập nhật hồ sơ, bao gồm tải lên hồ sơ người dùng. Sau đó, tôi đã thử nghiệm nó với người đưa thư, Ajax, PHP curl và laravel 5.6 làm phụ trợ.
Rohit Dhiman

14

Dường như luôn có một số nhầm lẫn về việc khi nào nên sử dụng HTTP POST so với phương thức HTTP PUT cho các dịch vụ REST. Hầu hết các nhà phát triển sẽ cố gắng liên kết các hoạt động CRUD trực tiếp với các phương thức HTTP. Tôi sẽ lập luận rằng điều này không đúng và người ta không thể đơn giản liên kết các khái niệm CRUD với các phương thức HTTP. Đó là:

Create => HTTP PUT
Retrieve => HTTP GET
Update => HTTP POST
Delete => HTTP DELETE

Đúng là R (etrieve) và D (elete) của các hoạt động CRUD có thể được ánh xạ trực tiếp tới các phương thức HTTP NHẬN và XÓA tương ứng. Tuy nhiên, sự nhầm lẫn nằm ở các hoạt động C (reate) và U (cập nhật). Trong một số trường hợp, người ta có thể sử dụng PUT để tạo trong khi trong các trường hợp khác, POST sẽ được yêu cầu. Sự không rõ ràng nằm trong định nghĩa của phương thức HTTP PUT so với phương thức POST HTTP.

Theo các thông số kỹ thuật HTTP 1.1, các phương thức GET, HEAD, DELETE và PUT phải là idempotent và phương thức POST không phải là idempotent. Điều đó có nghĩa là một hoạt động là bình thường nếu nó có thể được thực hiện trên một tài nguyên một lần hoặc nhiều lần và luôn trả về cùng một trạng thái của tài nguyên đó. Trong khi đó một hoạt động không bình thường có thể trả về trạng thái đã sửa đổi của tài nguyên từ yêu cầu này sang yêu cầu khác. Do đó, trong một hoạt động không bình thường, không có gì đảm bảo rằng người ta sẽ nhận được cùng một trạng thái của tài nguyên.

Dựa trên định nghĩa idempotent ở trên, tôi sử dụng phương thức HTTP PUT so với sử dụng phương thức POST HTTP cho các dịch vụ REST là: Sử dụng phương thức HTTP PUT khi:

The client includes all aspect of the resource including the unique identifier to uniquely identify the resource. Example: creating a new employee.
The client provides all the information for a resource to be able to modify that resource.This implies that the server side does not update any aspect of the resource (such as an update date).

Trong cả hai trường hợp, các thao tác này có thể được thực hiện nhiều lần với cùng kết quả. Đó là tài nguyên sẽ không được thay đổi bằng cách yêu cầu hoạt động nhiều lần. Do đó, một hoạt động bình thường thực sự. Sử dụng phương thức POST HTTP khi:

The server will provide some information concerning the newly created resource. For example, take a logging system. A new entry in the log will most likely have a numbering scheme which is determined on the server side. Upon creating a new log entry, the new sequence number will be determined by the server and not by the client.
On a modification of a resource, the server will provide such information as a resource state or an update date. Again in this case not all information was provided by the client and the resource will be changing from one modification request to the next. Hence a non idempotent operation.

Phần kết luận

Không tương quan trực tiếp và ánh xạ các hoạt động CRUD với các phương thức HTTP cho các dịch vụ REST. Việc sử dụng phương thức HTTP PUT so với phương thức HTTP POST phải dựa trên khía cạnh bình thường của hoạt động đó. Nghĩa là, nếu hoạt động là idempotent, thì hãy sử dụng phương thức HTTP PUT. Nếu hoạt động không bình thường, thì hãy sử dụng phương thức POST HTTP.


2
Cập nhật => HTTP POST: POST không phải để cập nhật
Premraj

@premraj Bạn đã đưa ra giả định rằng Burhan đang bảo bạn đừng làm; cụ thể là, bạn đang kết hợp CRUD, REST và HTTP. Nếu bạn đọc RFC 7231, trong đó những điều này được xác định, bạn sẽ thấy rằng trong giao thức HTTP, định nghĩa của POST chắc chắn cho phép cập nhật. Chỉ có các ràng buộc của REST nói khác đi.
IAM_AL_X

13

máy chủ gốc có thể tạo tài nguyên với URI đó

Vì vậy, bạn sử dụng POST và có thể, nhưng PUT không cần thiết cho việc tạo tài nguyên. Bạn không phải hỗ trợ cả hai. Đối với tôi POST là hoàn toàn đủ. Vì vậy, nó là một quyết định thiết kế.

Như trích dẫn của bạn đã đề cập, bạn sử dụng PUT để tạo không có tài nguyên nào được gán cho IRI và dù sao bạn cũng muốn tạo tài nguyên. Ví dụ: PUT /users/123/passwordthường thay thế mật khẩu cũ bằng mật khẩu mới, nhưng bạn có thể sử dụng nó để tạo mật khẩu nếu nó chưa tồn tại (ví dụ: bởi người dùng mới đăng ký hoặc khôi phục người dùng bị cấm).


Tôi nghĩ rằng bạn đã quản lý để cung cấp một trong một vài ví dụ hay về cách sử dụng PUT, được thực hiện tốt.
thecoshman

12

Tôi sẽ hạ cánh với những điều sau đây:

PUT đề cập đến một tài nguyên, được xác định bởi URI. Trong trường hợp này, bạn đang cập nhật nó. Nó là một phần của ba động từ đề cập đến tài nguyên - xóa và trở thành hai động từ còn lại.

POST về cơ bản là một tin nhắn dạng tự do, với ý nghĩa của nó được xác định là 'ngoài băng'. Nếu tin nhắn có thể được hiểu là thêm tài nguyên vào một thư mục, điều đó sẽ ổn, nhưng về cơ bản, bạn cần hiểu thông điệp bạn đang gửi (đăng) để biết điều gì sẽ xảy ra với tài nguyên.


Bởi vì PUT và GET và DELETE đề cập đến một tài nguyên, chúng cũng theo định nghĩa idempotent.

POST có thể thực hiện ba chức năng khác, nhưng sau đó ngữ nghĩa của yêu cầu sẽ bị mất trên các trung gian như bộ nhớ cache và proxy. Điều này cũng áp dụng để cung cấp bảo mật cho tài nguyên, vì URI của bài đăng không nhất thiết chỉ ra tài nguyên mà nó đang áp dụng (mặc dù có thể).

Một PUT không cần phải là một sáng tạo; dịch vụ có thể lỗi nếu tài nguyên chưa được tạo, nhưng nếu không thì cập nhật nó. Hoặc ngược lại - nó có thể tạo tài nguyên, nhưng không cho phép cập nhật. Điều duy nhất cần có về PUT là nó trỏ đến một tài nguyên cụ thể và tải trọng của nó là sự thể hiện của tài nguyên đó. PUT thành công có nghĩa là (chặn nhiễu) rằng một GET sẽ lấy cùng một tài nguyên.


Chỉnh sửa: Một điều nữa - PUT có thể tạo, nhưng nếu có thì ID phải là ID tự nhiên - AKA là địa chỉ email. Theo cách đó, khi bạn PUT hai lần, lần đặt thứ hai là một bản cập nhật của lần đầu tiên. Điều này làm cho nó idempotent .

Nếu ID được tạo (ví dụ: ID nhân viên mới), thì PUT thứ hai có cùng URL sẽ tạo một bản ghi mới, vi phạm quy tắc tạm thời. Trong trường hợp này, động từ sẽ là POST và thông điệp (không phải tài nguyên) sẽ tạo ra một tài nguyên bằng cách sử dụng các giá trị được xác định trong thông báo này.


9

Các ngữ nghĩa được cho là khác nhau, trong đó "PUT", như "GET" được coi là không có nghĩa - nghĩa là bạn có thể yêu cầu PUT chính xác nhiều lần và kết quả sẽ như thể bạn chỉ thực hiện một lần.

Tôi sẽ mô tả các quy ước mà tôi nghĩ được sử dụng rộng rãi nhất và hữu ích nhất:

Khi bạn PUT một tài nguyên tại một URL cụ thể, điều đó xảy ra là nó sẽ được lưu tại URL đó hoặc một cái gì đó dọc theo các dòng đó.

Khi bạn POST vào một tài nguyên tại một URL cụ thể, thường thì bạn sẽ đăng một phần thông tin liên quan lên URL đó. Điều này ngụ ý rằng tài nguyên tại URL đã tồn tại.

Ví dụ: khi bạn muốn tạo một luồng mới, bạn có thể PUT nó với một số URL. Nhưng khi bạn muốn POST một tin nhắn đến một luồng hiện có, bạn POST vào URL của nó.

Đối với việc sửa đổi các thuộc tính của luồng, bạn có thể làm điều đó với PUT hoặc POST. Về cơ bản, chỉ sử dụng "PUT" khi hoạt động không hoạt động - nếu không thì sử dụng POST.

Tuy nhiên, lưu ý rằng không phải tất cả các trình duyệt hiện đại đều hỗ trợ các động từ HTTP ngoài GET hoặc POST.


Những gì bạn mô tả POST thực sự là cách hành xử của PATCH. POST được cho là có nghĩa gì đó gần giống với "chắp thêm" như trong "danh sách gửi thư".
Alexander Torstling

8

Hầu hết thời gian, bạn sẽ sử dụng chúng như thế này:

  • POST một tài nguyên vào một bộ sưu tập
  • PUT một tài nguyên được xác định bởi bộ sưu tập /: id

Ví dụ:

  • POST / mục
  • PUT / vật phẩm / 1234

Trong cả hai trường hợp, phần thân yêu cầu chứa dữ liệu cho tài nguyên được tạo hoặc cập nhật. Rõ ràng từ tên tuyến đường rằng POST không phải là idempotent (nếu bạn gọi nó 3 lần thì nó sẽ tạo ra 3 đối tượng), nhưng PUT là idempotent (nếu bạn gọi nó là 3 lần thì kết quả là như nhau). PUT thường được sử dụng cho hoạt động "upsert" (tạo hoặc cập nhật), nhưng bạn luôn có thể trả về lỗi 404 nếu bạn chỉ muốn sử dụng nó để sửa đổi.

Lưu ý rằng POST "tạo" một phần tử mới trong bộ sưu tập và PUT "thay thế" một phần tử tại một URL nhất định, nhưng đó là một cách rất phổ biến để sử dụng PUT để sửa đổi một phần, nghĩa là chỉ sử dụng nó để cập nhật các tài nguyên hiện có và chỉ sửa đổi các trường được bao gồm trong cơ thể (bỏ qua các trường khác). Điều này là không chính xác về mặt kỹ thuật, nếu bạn muốn trở thành REST-purist, PUT nên thay thế toàn bộ tài nguyên và bạn nên sử dụng PATCH để cập nhật một phần. Cá nhân tôi không quan tâm nhiều đến hành vi rõ ràng và nhất quán trên tất cả các điểm cuối API của bạn.

Hãy nhớ rằng, REST là một tập hợp các quy ước và hướng dẫn để giữ cho API của bạn đơn giản. Nếu bạn kết thúc với một công việc phức tạp chỉ để kiểm tra hộp "RESTfull" thì bạn đang đánh bại mục đích;)


7

Mặc dù có thể có một cách bất khả tri để mô tả những điều này, nhưng nó dường như mâu thuẫn với các tuyên bố khác nhau từ câu trả lời cho các trang web.

Chúng ta hãy rất rõ ràng và trực tiếp ở đây. Nếu bạn là nhà phát triển .NET làm việc với API Web, thì thực tế là (từ tài liệu Microsoft API), http://www.asp.net/web-api/overview/creating-web-apis/creating-a-web -api-that-hỗ trợ-crud-hoạt động :

1. PUT = UPDATE (/api/products/id)
2. MCSD Exams 2014 -  UPDATE = PUT, there are **NO** multiple answers for that question period.

Chắc chắn bạn "có thể" sử dụng "POST" để cập nhật, nhưng chỉ cần tuân theo các quy ước được đặt ra cho bạn với khuôn khổ đã cho. Trong trường hợp của tôi, đó là .NET / Web API, vì vậy PUT dành cho CẬP NHẬT không có tranh luận.

Tôi hy vọng điều này sẽ giúp bất kỳ nhà phát triển nào của Microsoft đọc tất cả các nhận xét với các liên kết trang web của Amazon và Sun / Java.


7

Đây là một quy tắc đơn giản:

PUT cho một URL nên được sử dụng để cập nhật hoặc tạo tài nguyên có thể được đặt tại URL đó.

POST cho một URL nên được sử dụng để cập nhật hoặc tạo một tài nguyên được đặt tại một số URL ("cấp dưới") khác hoặc không thể định vị được qua HTTP.


1
PUT không phải để cập nhật, nó là để thay thế, lưu ý rằng để tạo ra bạn đang thay thế không có gì bằng một cái gì đó. POST hoàn toàn không phải để cập nhật dưới bất kỳ hình thức nào.
thecoshman

2
Liệu các thông số http nói rằng? Hay bạn đang căn cứ nhận xét của bạn về một cái gì đó khác?
Adam Griffiths

Đó chỉ là lẽ thường, làm thế nào bạn cập nhật một cái gì đó khi bạn không biết bạn đang cập nhật cái gì? POST là để tạo một tài nguyên mới.
thecoshman

2
thecoshman - bạn đang lạm dụng ngữ nghĩa ở đây - một sự thay thế có thể là một bản cập nhật nếu nó là cùng một tài nguyên với một vài khác biệt. Một thay thế chỉ có giá trị để đặt nếu thay thế được sử dụng để thay đổi cùng một tài nguyên. Thay thế bằng một tài nguyên mới và khác nhau là không hợp lệ (loại bỏ cũ và thêm mới?), Đặc biệt nếu tài nguyên 'mới' không có ID tự nhiên. POST, OTOH, là thứ có thể tạo, cập nhật, thay thế và xóa - sử dụng bài đăng tùy thuộc vào việc có thông báo để giải thích hay không, chẳng hạn như 'áp dụng giảm giá', có thể thay đổi hoặc không thay đổi tài nguyên tùy thuộc vào Hợp lý.
Gerard ONeill

Đối với nhận xét thứ hai của bạn - làm thế nào về việc bạn 'lấy' tài nguyên, sửa đổi các trường bạn cần và sau đó đặt lại? Hoặc về việc nếu tài nguyên đến từ một nguồn khác nhưng sử dụng ID tự nhiên (ID bên ngoài) - thì sẽ tự động cập nhật tài nguyên tại URL khi dữ liệu gốc thay đổi.
Gerard ONeill

6

Nếu bạn quen thuộc với các hoạt động cơ sở dữ liệu, có

  1. Lựa chọn
  2. Chèn
  3. Cập nhật
  4. Xóa bỏ
  5. Hợp nhất (Cập nhật nếu đã có, chèn khác)

Tôi sử dụng PUTđể Hợp nhất và cập nhật như các hoạt động và sử dụng POSTcho Insertions.


5

Trong thực tế, POST hoạt động tốt để tạo tài nguyên. URL của tài nguyên mới được tạo phải được trả về trong tiêu đề phản hồi Vị trí. PUT nên được sử dụng để cập nhật tài nguyên hoàn toàn. Vui lòng hiểu rằng đây là những cách thực hành tốt nhất khi thiết kế API RESTful. Đặc tả HTTP như vậy không hạn chế sử dụng PUT / POST với một vài hạn chế để tạo / cập nhật tài nguyên. Hãy xem http://techoctave.com/c7/posts/71-twitter-rest-api-dissected để tóm tắt các thực tiễn tốt nhất.


Đối với hầu hết các phần, từ đọc qua tất cả các tiếng ồn này, bạn dường như trên quả bóng. Tuy nhiên, tôi muốn nói rằng, chúng ta nên coi PUT là phương thức thay thế, thay vì tạo / cập nhật. Tôi nghĩ rằng nó mô tả tốt hơn trong một những gì nó làm.
thecoshman
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.