Sử dụng các phương thức PUT so với PATCH trong các tình huống thực của API REST

740
Dmitry Kudryavtsev 2015-02-12 06:30.

Trước hết, một số định nghĩa:

PUT được định nghĩa trong Phần 9.6 RFC 2616 :

Phương thức PUT yêu cầu thực thể kèm theo được lưu trữ theo URI yêu cầu được cung cấp. Nếu Request-URI đề 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 nằm 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 tác nhân người dùng yêu cầu xác định là tài nguyên mới, thì máy chủ gốc có thể tạo tài nguyên bằng URI đó.

PATCH được định nghĩa trong RFC 5789 :

Phương thức PATCH yêu cầu một tập hợp các thay đổi được mô tả trong thực thể yêu cầu được áp dụng cho tài nguyên được xác định bởi URI Yêu cầu.

Cũng theo RFC 2616 Mục 9.1.2 PUT là Idempotent trong khi PATCH thì không.

Bây giờ chúng ta hãy xem một ví dụ thực tế. Khi tôi thực hiện POST tới /usersvới dữ liệu {username: 'skwee357', email: '[email protected]'}và máy chủ có khả năng tạo tài nguyên, nó sẽ phản hồi với 201 và vị trí tài nguyên (giả sử /users/1) và mọi lệnh gọi tiếp theo tới GET /users/1sẽ trả về {id: 1, username: 'skwee357', email: '[email protected]'}.

Bây giờ hãy để chúng tôi nói rằng tôi muốn sửa đổi email của mình. Sửa đổi email được coi là "một tập hợp các thay đổi" và do đó tôi nên XEM /users/1với " tài liệu vá lỗi ". Trong trường hợp của tôi nó sẽ là tài liệu json: {email: '[email protected]'}. Sau đó máy chủ trả về 200 (giả sử quyền được chấp nhận). Điều này đưa tôi đến câu hỏi đầu tiên:

  • PATCH KHÔNG phải là không cố định. Nó nói như vậy trong RFC 2616 và RFC 5789. Tuy nhiên, nếu tôi đưa ra cùng một yêu cầu PATCH (với email mới của tôi), tôi sẽ nhận được trạng thái tài nguyên tương tự (với email của tôi được sửa đổi thành giá trị được yêu cầu). Tại sao PATCH không phải là Idempotent?

PATCH là một động từ tương đối mới (được RFC giới thiệu vào tháng 3 năm 2010), và nó dùng để giải quyết vấn đề "vá" hoặc sửa đổi một tập hợp các trường. Trước khi PATCH được giới thiệu, mọi người đều sử dụng PUT để cập nhật tài nguyên. Nhưng sau khi PATCH được giới thiệu, nó khiến tôi bối rối không biết PUT được sử dụng để làm gì. Và điều này đưa tôi đến câu hỏi thứ hai (và chính) của tôi:

  • Sự khác biệt thực sự giữa PUT và PATCH là gì? Tôi đã đọc ở đâu đó rằng PUT có thể được sử dụng để thay thế toàn bộ thực thể trong tài nguyên cụ thể, vì vậy người ta nên gửi toàn bộ thực thể (thay vì tập hợp các thuộc tính như với PATCH). Cách sử dụng thực tế thực sự cho trường hợp như vậy là gì? Khi nào bạn muốn thay thế / ghi đè một thực thể tại một URI tài nguyên cụ thể và tại sao một thao tác như vậy không được coi là cập nhật / vá thực thể? Trường hợp sử dụng thực tế duy nhất mà tôi thấy cho PUT là phát hành PUT trên một tập hợp, tức là /usersđể thay thế toàn bộ tập hợp. Việc phát hành PUT trên một thực thể cụ thể không có ý nghĩa gì sau khi PATCH được giới thiệu. Tôi có lầm không?

10 answers

1012
Dan Lowe 2015-12-22 06:20.

LƯU Ý : Khi lần đầu tiên tôi dành thời gian đọc về REST, tưởng tượng là một khái niệm khó hiểu để cố gắng hiểu đúng. Tôi vẫn không hiểu nó hoàn toàn đúng trong câu trả lời ban đầu của mình, vì các nhận xét khác (và câu trả lời của Jason Hoetger ) đã cho thấy. Trong một thời gian, tôi đã chống lại việc cập nhật câu trả lời này một cách rộng rãi, để tránh Jason ăn cắp ý tưởng một cách hiệu quả, nhưng tôi đang chỉnh sửa nó ngay bây giờ bởi vì, tôi được yêu cầu (trong phần bình luận).

Sau khi đọc câu trả lời của tôi, tôi khuyên bạn cũng nên đọc câu trả lời xuất sắc của Jason Hoetger cho câu hỏi này và tôi sẽ cố gắng làm cho câu trả lời của mình tốt hơn mà không chỉ đơn giản là ăn cắp của Jason.

Tại sao PUT là Idempotent?

Như bạn đã lưu ý trong trích dẫn RFC 2616 của mình, PUT được coi là giá trị trung tâm. Khi bạn PUT một tài nguyên, hai giả định này được áp dụng:

  1. Bạn đang đề cập đến một thực thể, không phải một bộ sưu tập.

  2. Thực thể bạn đang cung cấp đã hoàn tất ( toàn bộ thực thể).

Hãy xem một trong những ví dụ của bạn.

{ "username": "skwee357", "email": "[email protected]" }

Nếu bạn ĐĂNG tài liệu này lên /users, như bạn đề xuất, thì bạn có thể nhận lại một thực thể chẳng hạn như

## /users/1

{
    "username": "skwee357",
    "email": "[email protected]"
}

Nếu bạn muốn sửa đổi thực thể này sau đó, bạn chọn giữa PUT và PATCH. PUT có thể trông như thế này:

PUT /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // new email address
}

Bạn có thể thực hiện điều tương tự bằng cách sử dụng PATCH. Nó có thể trông như thế này:

PATCH /users/1
{
    "email": "[email protected]"       // new email address
}

Bạn sẽ nhận thấy sự khác biệt ngay lập tức giữa hai điều này. PUT bao gồm tất cả các tham số trên người dùng này, nhưng PATCH chỉ bao gồm một tham số đang được sửa đổi ( email).

Khi sử dụng PUT, giả sử rằng bạn đang gửi thực thể hoàn chỉnh và thực thể hoàn chỉnh đó sẽ thay thế mọi thực thể hiện có tại URI đó. Trong ví dụ trên, PUT và PATCH thực hiện cùng một mục tiêu: cả hai đều thay đổi địa chỉ email của người dùng này. Nhưng PUT xử lý nó bằng cách thay thế toàn bộ thực thể, trong khi PATCH chỉ cập nhật các trường đã được cung cấp, để lại các trường khác.

Vì các yêu cầu PUT bao gồm toàn bộ thực thể, nếu bạn đưa ra cùng một yêu cầu lặp đi lặp lại, yêu cầu đó sẽ luôn có cùng một kết quả (dữ liệu bạn đã gửi bây giờ là toàn bộ dữ liệu của thực thể). Do đó PUT là giá trị trung tâm.

Sử dụng PUT sai

Điều gì xảy ra nếu bạn sử dụng dữ liệu PATCH ở trên trong một yêu cầu PUT?

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}
PUT /users/1
{
    "email": "[email protected]"       // new email address
}

GET /users/1
{
    "email": "[email protected]"      // new email address... and nothing else!
}

(Tôi giả sử cho mục đích của câu hỏi này rằng máy chủ không có bất kỳ trường bắt buộc cụ thể nào và sẽ cho phép điều này xảy ra ... điều đó có thể không đúng trong thực tế.)

Vì chúng tôi đã sử dụng PUT, nhưng chỉ được cung cấp email, bây giờ đó là thứ duy nhất trong thực thể này. Điều này đã dẫn đến mất dữ liệu.

Ví dụ này ở đây cho mục đích minh họa - đừng bao giờ thực sự làm điều này. Yêu cầu PUT này là lý tưởng về mặt kỹ thuật, nhưng điều đó không có nghĩa là nó không phải là một ý tưởng tồi tệ, hỏng hóc.

Làm thế nào để PATCH có thể trở thành idmpotent?

Trong ví dụ trên, PATCH không quan trọng. Bạn đã thực hiện một thay đổi, nhưng nếu bạn thực hiện lại cùng một thay đổi, nó sẽ luôn trả về cùng một kết quả: bạn đã thay đổi địa chỉ email thành giá trị mới.

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}
PATCH /users/1
{
    "email": "[email protected]"       // new email address
}

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // email address was changed
}
PATCH /users/1
{
    "email": "[email protected]"       // new email address... again
}

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // nothing changed since last GET
}

Ví dụ ban đầu của tôi, đã sửa để đảm bảo độ chính xác

Ban đầu, tôi có các ví dụ mà tôi nghĩ là hiển thị không hợp lý, nhưng chúng đã gây hiểu lầm / không chính xác. Tôi sẽ giữ lại các ví dụ, nhưng sử dụng chúng để minh họa một điều khác: nhiều tài liệu PATCH dựa trên cùng một thực thể, sửa đổi các thuộc tính khác nhau, không làm cho các PATCH không phải là giá trị trung tâm.

Giả sử rằng vào một thời điểm nào đó, một người dùng đã được thêm vào. Đây là trạng thái mà bạn đang bắt đầu.

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

Sau PATCH, bạn có một thực thể được sửa đổi:

PATCH /users/1
{"email": "[email protected]"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",    // the email changed, yay!
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

Nếu sau đó bạn liên tục áp dụng PATCH của mình, bạn sẽ tiếp tục nhận được kết quả tương tự: email đã được thay đổi thành giá trị mới. A đi vào, A đi ra, do đó đây là iđêan.

Một giờ sau, sau khi bạn đã đi pha một ít cà phê và nghỉ ngơi, một người khác đến cùng với PATCH của chính họ. Có vẻ như Bưu điện đã thực hiện một số thay đổi.

PATCH /users/1
{"zip": "12345"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",  // still the new email you set
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"                      // and this change as well
}

Vì PATCH này từ bưu điện không liên quan đến email, chỉ có mã zip, nếu nó được áp dụng nhiều lần, nó cũng sẽ nhận được kết quả tương tự: mã zip được đặt thành giá trị mới. A đi vào, A đi ra, do đó đây cũng là iđêan.

Ngày hôm sau, bạn quyết định gửi lại PATCH của mình.

PATCH /users/1
{"email": "[email protected]"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"
}

Bản vá của bạn có tác dụng tương tự như ngày hôm qua: nó thiết lập địa chỉ email. A đi vào, A đi ra, do đó đây cũng là ý tưởng.

Tôi đã sai gì trong câu trả lời ban đầu của mình

Tôi muốn rút ra một điểm khác biệt quan trọng (điều gì đó tôi đã sai trong câu trả lời ban đầu của mình). Nhiều máy chủ sẽ phản hồi các yêu cầu REST của bạn bằng cách gửi lại trạng thái thực thể mới cùng với các sửa đổi của bạn (nếu có). Vì vậy, khi bạn nhận được phản hồi này , nó khác với phản hồi bạn nhận được ngày hôm qua , vì mã zip không phải là mã bạn nhận được lần trước. Tuy nhiên, yêu cầu của bạn không liên quan đến mã zip, chỉ liên quan đến email. Vì vậy, tài liệu PATCH của bạn vẫn còn nguyên vẹn - email bạn đã gửi trong PATCH hiện là địa chỉ email trên thực thể.

Vậy khi nào thì PATCH không phải là giá trị trung tâm?

Để có cách xử lý đầy đủ cho câu hỏi này, một lần nữa tôi giới thiệu bạn đến câu trả lời của Jason Hoetger . Tôi sẽ chỉ để nó ở đó, bởi vì tôi thực sự không nghĩ rằng tôi có thể trả lời phần này tốt hơn anh ấy đã có.

355
Jason Hoetger 2016-09-06 12:24.

Mặc dù câu trả lời xuất sắc của Dan Lowe đã trả lời rất cặn kẽ câu hỏi của OP về sự khác biệt giữa PUT và PATCH, câu trả lời cho câu hỏi tại sao PATCH không phải là Idempotent không hoàn toàn chính xác.

Để chỉ ra lý do tại sao PATCH không phải là aimmpotent, bạn nên bắt đầu bằng định nghĩa về Idempotence (từ Wikipedia ):

Thuật ngữ Idempotent được sử dụng toàn diện hơn để mô tả một phép toán sẽ tạo ra kết quả giống nhau nếu được thực hiện một lần hoặc nhiều lần [...] Một hàm iđêan là một hàm có thuộc tính f (f (x)) = f (x) for giá trị x bất kỳ.

Trong ngôn ngữ dễ tiếp cận hơn, một PATCH không thể thay đổi được có thể được định nghĩa là: Sau khi PATCH một tài nguyên bằng tài liệu vá, tất cả các lệnh gọi PATCH tiếp theo đến cùng một tài nguyên với cùng một tài liệu vá sẽ không thay đổi tài nguyên.

Ngược lại, một hoạt động không phải là một phép toán có giá trị trong đó f (f (x))! = F (x), đối với PATCH có thể được phát biểu là: Sau khi PATCH một tài nguyên với một tài liệu vá, PATCH tiếp theo sẽ gọi đến cùng một tài nguyên với cùng tài liệu vá làm thay đổi tài nguyên.

Để minh họa một PATCH không phải là định hướng, giả sử có tài nguyên / người dùng và giả sử rằng việc gọi GET /userstrả về một danh sách người dùng, hiện tại:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" }]

Thay vì PATCHing / users / {id}, như trong ví dụ của OP, giả sử máy chủ cho phép PATCHing / users. Hãy đưa ra yêu cầu PATCH này:

PATCH /users
[{ "op": "add", "username": "newuser", "email": "[email protected]" }]

Tài liệu vá lỗi của chúng tôi hướng dẫn máy chủ thêm người dùng mới được gọi newuservào danh sách người dùng. Sau khi gọi lần đầu tiên, GET /userssẽ trả về:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
 { "id": 2, "username": "newuser", "email": "[email protected]" }]

Bây giờ, nếu chúng tôi đưa ra yêu cầu PATCH chính xác như trên, điều gì sẽ xảy ra? (Vì lợi ích của ví dụ này, hãy giả sử rằng tài nguyên / users cho phép các tên người dùng trùng lặp.) "Op" là "thêm", vì vậy một người dùng mới được thêm vào danh sách và GET /userstrả về sau :

[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
 { "id": 2, "username": "newuser", "email": "[email protected]" },
 { "id": 3, "username": "newuser", "email": "[email protected]" }]

Tài nguyên / người dùng đã thay đổi một lần nữa , mặc dù chúng tôi đã phát hành cùng một PATCH chính xác với cùng một điểm cuối. Nếu PATCH của chúng ta là f (x), f (f (x)) không giống với f (x), và do đó, PATCH cụ thể này không phải là giá trị ngẫu nhiên .

Mặc dù PATCH không được đảm bảo là không cần thiết, nhưng không có gì trong đặc tả PATCH ngăn bạn thực hiện tất cả các hoạt động PATCH trên máy chủ lý tưởng cụ thể của bạn. RFC 5789 thậm chí còn dự đoán những lợi thế từ các yêu cầu PATCH không cần thiết:

Một yêu cầu PATCH có thể được đưa ra theo cách giống như là không thể thay thế được, điều này cũng giúp ngăn chặn các kết quả xấu do va chạm giữa hai yêu cầu PATCH trên cùng một tài nguyên trong cùng một khung thời gian.

Trong ví dụ của Dan, hoạt động PATCH của anh ấy, trên thực tế, là không có ý nghĩa. Trong ví dụ đó, thực thể / users / 1 đã thay đổi giữa các yêu cầu PATCH của chúng tôi, nhưng không phải do các yêu cầu PATCH của chúng tôi; nó thực sự là tài liệu vá lỗi khác nhau của Bưu điện đã khiến mã zip thay đổi. PATCH khác nhau của Bưu điện là một hoạt động khác; nếu PATCH của chúng ta là f (x), PATCH của Bưu điện là g (x). Idempotence tuyên bố điều đó f(f(f(x))) = f(x), nhưng không bảo đảm về điều đó f(g(f(x))).

77
Kalel Wade 2015-04-11 08:53.

Tôi cũng tò mò về điều này và tìm thấy một vài bài báo thú vị. Tôi có thể không trả lời câu hỏi của bạn ở mức độ đầy đủ, nhưng điều này ít nhất cung cấp thêm một số thông tin.

http://restful-api-design.readthedocs.org/en/latest/methods.html

HTTP RFC chỉ định rằng PUT phải lấy một biểu diễn tài nguyên mới đầy đủ làm thực thể yêu cầu. Điều này có nghĩa là nếu ví dụ chỉ một số thuộc tính được cung cấp, thì những thuộc tính đó sẽ bị xóa (tức là được đặt thành null).

Do đó, PUT sẽ gửi toàn bộ đối tượng. Ví dụ,

/users/1
PUT {id: 1, username: 'skwee357', email: '[email protected]'}

Điều này sẽ cập nhật email một cách hiệu quả. Lý do PUT có thể không quá hiệu quả là việc bạn chỉ thực sự sửa đổi một trường và bao gồm tên người dùng là vô dụng. Ví dụ tiếp theo cho thấy sự khác biệt.

/users/1
PUT {id: 1, email: '[email protected]'}

Bây giờ, nếu PUT được thiết kế theo thông số kỹ thuật, thì PUT sẽ đặt tên người dùng thành null và bạn sẽ nhận được thông tin sau.

{id: 1, username: null, email: '[email protected]'}

Khi bạn sử dụng PATCH, bạn chỉ cập nhật trường bạn chỉ định và để yên phần còn lại như trong ví dụ của bạn.

Những điều sau đây về PATCH hơi khác so với những gì tôi chưa từng thấy trước đây.

http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/

Sự khác biệt giữa yêu cầu PUT và PATCH được phản ánh trong cách máy chủ xử lý thực thể kèm theo để sửa đổi tài nguyên được xác định bởi Request-URI. Trong một yêu cầu PUT, thực thể kèm theo được coi là phiên bản sửa đổi của tài nguyên được lưu trữ trên máy chủ gốc và ứng dụng khách đang yêu cầu thay thế phiên bản được lưu trữ. Tuy nhiên, với PATCH, thực thể kèm theo chứa một tập hợp các hướng dẫn mô tả cách tài nguyên hiện đang nằm trên máy chủ gốc sẽ được sửa đổi để tạo ra một phiên bản mới. Phương thức PATCH ảnh hưởng đến tài nguyên được xác định bởi URI Yêu cầu và nó cũng CÓ THỂ có tác dụng phụ đối với các tài nguyên khác; tức là, các tài nguyên mới có thể được tạo ra hoặc những tài nguyên hiện có được sửa đổi bằng cách áp dụng PATCH.

PATCH /users/123

[
    { "op": "replace", "path": "/email", "value": "[email protected]" }
]

Bạn ít nhiều coi PATCH như một cách để cập nhật một trường. Vì vậy, thay vì gửi qua đối tượng một phần, bạn đang gửi qua hoạt động. tức là Thay thế email bằng giá trị.

Bài báo kết thúc với điều này.

Điều đáng nói là PATCH không thực sự được thiết kế cho các API REST thực sự, vì luận án của Fielding không xác định bất kỳ cách nào để sửa đổi một phần tài nguyên. Tuy nhiên, bản thân Roy Fielding nói rằng PATCH là thứ mà [ông] tạo ra cho đề xuất HTTP / 1.1 ban đầu vì một phần PUT không bao giờ RESTful. Chắc chắn rằng bạn không chuyển một bản đại diện hoàn chỉnh, nhưng dù sao thì REST cũng không yêu cầu bản trình bày phải hoàn chỉnh.

Bây giờ, tôi không biết liệu tôi có đặc biệt đồng ý với bài báo như nhiều nhà bình luận chỉ ra hay không. Gửi qua một phần đại diện có thể dễ dàng là một mô tả về những thay đổi.

Đối với tôi, tôi hỗn hợp khi sử dụng PATCH. Đối với hầu hết các phần, tôi sẽ coi PUT là một PATCH vì sự khác biệt thực sự duy nhất mà tôi nhận thấy cho đến nay là PUT "nên" đặt các giá trị bị thiếu thành null. Nó có thể không phải là cách 'đúng nhất' để làm điều đó, nhưng chúc may mắn mã hóa hoàn hảo.

22
Bijan 2019-10-18 08:55.

TLDR - Phiên bản Dumbed Down

PUT => Đặt tất cả các thuộc tính mới cho một tài nguyên hiện có.

PATCH => Cập nhật một phần tài nguyên hiện có (không yêu cầu tất cả các thuộc tính).

20
Bin Ni 2017-05-18 09:01.

Sự khác biệt giữa PUT và PATCH là:

  1. PUT được yêu cầu phải là giá trị trung tâm. Để đạt được điều đó, bạn phải đưa toàn bộ tài nguyên hoàn chỉnh vào phần thân yêu cầu.
  2. PATCH có thể không phải là giá trị trung tâm. Điều đó ngụ ý rằng nó cũng có thể không có ích trong một số trường hợp, chẳng hạn như các trường hợp bạn đã mô tả.

PATCH yêu cầu một số "ngôn ngữ vá lỗi" để cho máy chủ biết cách sửa đổi tài nguyên. Người gọi và máy chủ cần xác định một số "hoạt động" như "thêm", "thay thế", "xóa". Ví dụ:

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "state": "NY",
  "zip": "10001"
}

PATCH /contacts/1
{
 [{"operation": "add", "field": "address", "value": "123 main street"},
  {"operation": "replace", "field": "email", "value": "[email protected]"},
  {"operation": "delete", "field": "zip"}]
}

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "state": "NY",
  "address": "123 main street",
}

Thay vì sử dụng các trường "hoạt động" rõ ràng, ngôn ngữ vá lỗi có thể làm cho nó ẩn bằng cách xác định các quy ước như:

trong phần thân yêu cầu PATCH:

  1. Sự tồn tại của một trường có nghĩa là "thay thế" hoặc "thêm" trường đó.
  2. Nếu giá trị của một trường là null, nghĩa là xóa trường đó.

Với quy ước trên, PATCH trong ví dụ có thể có dạng sau:

PATCH /contacts/1
{
  "address": "123 main street",
  "email": "[email protected]",
  "zip":
}

Cái nào trông ngắn gọn và thân thiện hơn. Nhưng người dùng cần lưu ý về quy ước cơ bản.

Với các hoạt động mà tôi đã đề cập ở trên, PATCH vẫn là không cố định. Nhưng nếu bạn định nghĩa các phép toán như: "increment" hoặc "append", bạn có thể dễ dàng thấy nó sẽ không còn là idmpotent nữa.

3
Rolvernew 2018-11-16 02:51.

Hãy để tôi trích dẫn và nhận xét kỹ hơn phần RFC 7231 4.2.2 , đã được trích dẫn trong các nhận xét trước đó:

Một phương thức yêu cầu được coi là "idempotent" nếu hiệu ứng dự kiến ​​trên máy chủ của nhiều yêu cầu giống hệt nhau với phương thức đó giống như hiệu ứng đối với một yêu cầu như vậy. Trong số các phương thức yêu cầu được xác định bởi đặc tả này, PUT, DELETE và các phương thức yêu cầu an toàn là không quan trọng.

(...)

Các phương thức Idempotent được phân biệt vì yêu cầu có thể được lặp lại tự động nếu xảy ra lỗi giao tiếp trước khi máy khách có thể đọc phản hồi của máy chủ. Ví dụ: nếu một máy khách gửi một yêu cầu PUT và kết nối bên dưới bị đóng trước khi nhận được bất kỳ phản hồi nào, thì máy khách có thể thiết lập một kết nối mới và thử lại yêu cầu Idempotent. Nó biết rằng việc lặp lại yêu cầu sẽ có cùng tác dụng dự kiến, ngay cả khi yêu cầu ban đầu thành công, mặc dù phản hồi có thể khác.

Vì vậy, những gì sẽ là "giống nhau" sau khi một yêu cầu lặp đi lặp lại của một phương thức Idempotent? Không phải trạng thái máy chủ, cũng không phải phản hồi của máy chủ, mà là hiệu ứng dự kiến . Đặc biệt, phương pháp này nên được đánh giá cao "từ quan điểm của khách hàng". Bây giờ, tôi nghĩ rằng quan điểm này cho thấy rằng ví dụ cuối cùng trong câu trả lời của Dan Lowe , mà tôi không muốn đạo văn ở đây, thực sự cho thấy rằng một yêu cầu PATCH có thể không phải là ý tưởng (theo cách tự nhiên hơn so với ví dụ trong Câu trả lời của Jason Hoetger ).

Thật vậy, hãy làm cho ví dụ chính xác hơn một chút bằng cách đưa ra một ý định rõ ràng có thể cho khách hàng đầu tiên. Giả sử rằng khách hàng này xem qua danh sách người dùng với dự án để kiểm tra email mã zip của họ . Anh ấy bắt đầu với người dùng 1, nhận thấy rằng zip đúng nhưng email sai. Anh ấy quyết định sửa lỗi này bằng một yêu cầu PATCH, yêu cầu này hoàn toàn hợp pháp và chỉ gửi

PATCH /users/1
{"email": "[email protected]"}

vì đây là lần chỉnh sửa duy nhất. Bây giờ, yêu cầu không thành công do một số vấn đề mạng và được gửi lại tự động sau vài giờ. Trong khi đó, một ứng dụng khách khác đã (sai) sửa đổi mã zip của người dùng 1. Sau đó, việc gửi cùng một yêu cầu PATCH lần thứ hai không đạt được hiệu quả như dự kiến của ứng dụng, vì chúng tôi kết thúc với một mã zip không chính xác. Do đó, phương pháp này không phải là giá trị trung tâm theo nghĩa của RFC.

Thay vào đó, nếu khách hàng sử dụng yêu cầu PUT để sửa email, gửi đến máy chủ tất cả các thuộc tính của người dùng 1 cùng với email, thì hiệu quả dự kiến ​​của anh ta sẽ đạt được ngay cả khi yêu cầu phải được gửi lại sau đó và người dùng 1 đã được sửa đổi trong khi đó --- vì yêu cầu PUT thứ hai sẽ ghi đè lên tất cả các thay đổi kể từ yêu cầu đầu tiên.

3
Zbigniew Szczęsny 2019-09-20 13:25.

Theo ý kiến ​​khiêm tốn của tôi, sự bất lực có nghĩa là:

  • ĐẶT:

Tôi gửi một định nghĩa tài nguyên cạnh tranh, vì vậy - trạng thái tài nguyên kết quả chính xác như được xác định bởi các tham số PUT. Mỗi lần tôi cập nhật tài nguyên với cùng một tham số PUT - trạng thái kết quả hoàn toàn giống nhau.

  • VÁ:

Tôi chỉ gửi một phần định nghĩa tài nguyên, vì vậy có thể xảy ra trường hợp người dùng khác đang cập nhật các thông số KHÁC của tài nguyên này trong thời gian chờ đợi. Do đó - các bản vá liên tiếp có cùng tham số và giá trị của chúng có thể dẫn đến trạng thái tài nguyên khác nhau. Ví dụ:

Giả sử một đối tượng được xác định như sau:

XE: - màu: đen, - loại: sedan, - chỗ: 5

Tôi vá nó bằng:

{màu đỏ'}

Đối tượng kết quả là:

XE: - màu: đỏ, - loại: sedan, - chỗ: 5

Sau đó, một số người dùng khác vá chiếc xe này bằng:

{type: 'hatchback'}

vì vậy, đối tượng kết quả là:

XE: - màu: đỏ, - loại: hatchback, - chỗ: 5

Bây giờ, nếu tôi vá lại đối tượng này bằng:

{màu đỏ'}

đối tượng kết quả là:

XE: - màu: đỏ, - loại: hatchback, - chỗ: 5

KHÁC BIỆT với những gì tôi đã có trước đây!

Đây là lý do tại sao PATCH không phải là aimmpotent trong khi PUT là Identotent.

1
Mohammad-Ali A'RÂBI 2019-11-01 03:19.

Để kết thúc cuộc thảo luận về tính đơn giản, tôi cần lưu ý rằng người ta có thể xác định độ tin cậy trong ngữ cảnh REST theo hai cách. Trước tiên, hãy chính thức hóa một số điều:

Một tài nguyên là một hàm với codomain của nó là lớp các chuỗi. Nói cách khác, tài nguyên là một tập hợp con String × Any, nơi tất cả các khóa là duy nhất. Hãy gọi lớp của các tài nguyên Res.

Một hoạt động REST trên tài nguyên, là một hàm f(x: Res, y: Res): Res. Hai ví dụ về hoạt động REST là:

  • PUT(x: Res, y: Res): Res = x
  • PATCH(x: Res, y: Res): Res, hoạt động như thế nào PATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}.

(Định nghĩa này được thiết kế đặc biệt để tranh luận về PUTPOST, ví dụ: không có nhiều ý nghĩa về GETPOST, vì nó không quan tâm đến tính bền bỉ).

Bây giờ, bằng cách sửa chữa x: Res(nói một cách không chính thức, sử dụng currying), PUT(x: Res)PATCH(x: Res)là các hàm đơn biến của kiểu Res → Res.

  1. Một chức năng g: Res → Resđược gọi là idempotent toàn cầu , khi g ○ g == g, tức là đối với bất kỳ y: Res, g(g(y)) = g(y).

  2. Hãy để x: Resmột nguồn lực, và k = x.keys. Một hàm g = f(x)được gọi là giá trị định vị bên trái , khi với mỗi hàm y: Res, chúng ta có g(g(y))|ₖ == g(y)|ₖ. Về cơ bản, nó có nghĩa là kết quả sẽ giống nhau, nếu chúng ta nhìn vào các phím được áp dụng.

Vì vậy, PATCH(x)không phải là định hướng toàn cục, mà là định hướng trái. Và tính không chính xác bên trái là điều quan trọng ở đây: nếu chúng tôi vá một vài khóa của tài nguyên, chúng tôi muốn các khóa đó giống nhau nếu chúng tôi vá lại và chúng tôi không quan tâm đến phần còn lại của tài nguyên.

Và khi RFC đang nói về việc PATCH không phải là giá trị trung bình, thì nó đang nói về tính tổng hợp toàn cầu. Chà, thật tốt khi nó không phải là công cụ vô định toàn cầu, nếu không thì nó sẽ là một hoạt động hỏng.


Bây giờ, câu trả lời của Jason Hoetger đang cố gắng chứng minh rằng PATCH thậm chí không phải là yếu tố quan trọng, nhưng nó đang phá vỡ quá nhiều thứ để làm như vậy:

  • Trước hết, PATCH được sử dụng trên một tập hợp, mặc dù PATCH được định nghĩa để hoạt động trên bản đồ / từ điển / các đối tượng khóa-giá trị.
  • Nếu ai đó thực sự muốn áp dụng PATCH cho các tập hợp, thì có một bản dịch tự nhiên nên được sử dụng t: Set<T> → Map<T, Boolean>:, được định nghĩa với x in A iff t(A)(x) == True. Sử dụng định nghĩa này, việc vá lỗi là không quan trọng.
  • Trong ví dụ, bản dịch này không được sử dụng, thay vào đó, PATCH hoạt động giống như BÀI ĐĂNG. Trước hết, tại sao một ID được tạo cho đối tượng? Và nó được tạo ra khi nào? Nếu đối tượng được so sánh đầu tiên với các phần tử của tập hợp và nếu không tìm thấy đối tượng phù hợp nào thì ID được tạo, sau đó một lần nữa chương trình sẽ hoạt động khác ( {id: 1, email: "[email protected]"}phải khớp với {email: "[email protected]"}, nếu không chương trình luôn bị hỏng và không thể PATCH vá). Nếu ID được tạo trước khi kiểm tra so với tập hợp, một lần nữa chương trình bị hỏng.

Người ta có thể đưa ra các ví dụ về PUT không phải là không cố định với việc phá vỡ một nửa số thứ bị hỏng trong ví dụ này:

  • Một ví dụ với các tính năng bổ sung được tạo sẽ là lập phiên bản. Người ta có thể ghi lại số lượng thay đổi trên một đối tượng. Trong trường hợp này, PUT không phải là không cố định: PUT /user/12 {email: "[email protected]"}kết quả trong {email: "...", version: 1}lần đầu tiên và {email: "...", version: 2}lần thứ hai.
  • Lộn xộn với các ID, người ta có thể tạo một ID mới mỗi khi đối tượng được cập nhật, dẫn đến một PUT không phải là định vị.

Tất cả các ví dụ trên là những ví dụ tự nhiên mà người ta có thể gặp phải.


Điểm cuối cùng của tôi là, PATCH không nên có giá trị toàn cầu , nếu không sẽ không mang lại cho bạn hiệu quả mong muốn. Bạn muốn thay đổi địa chỉ email của người dùng của mình mà không cần chạm vào phần còn lại của thông tin và bạn không muốn ghi đè các thay đổi của một bên khác đang truy cập vào cùng một tài nguyên.

0
Eric Wood 2020-07-14 11:30.

Mọi người khác đã trả lời PUT vs PATCH. Tôi chỉ định trả lời phần tiêu đề của câu hỏi ban đầu: "... trong các tình huống thực tế của API REST". Trong thế giới thực, điều này đã xảy ra với tôi với ứng dụng internet có máy chủ RESTful và cơ sở dữ liệu quan hệ với bảng Khách hàng "rộng" (khoảng 40 cột). Tôi đã nhầm lẫn khi sử dụng PUT nhưng đã cho rằng nó giống như lệnh SQL Update và chưa điền vào tất cả các cột. Vấn đề: 1) Một số cột là tùy chọn (vì vậy trống là câu trả lời hợp lệ), 2) nhiều cột hiếm khi thay đổi, 3) một số cột người dùng không được phép thay đổi, chẳng hạn như dấu thời gian của Ngày mua cuối cùng, 4) một cột là miễn phí -dạng văn bản cột "Nhận xét" mà người dùng chăm chỉ điền vào nửa trang nhận xét dịch vụ khách hàng như tên vợ / chồng để hỏi về HOẶC đơn đặt hàng thông thường, 5) Tôi đang làm việc trên một ứng dụng internet và có lo lắng về kích thước gói.

Nhược điểm của PUT là nó buộc bạn phải gửi một gói thông tin lớn (tất cả các cột bao gồm toàn bộ cột Nhận xét, mặc dù chỉ thay đổi một số thứ) VÀ vấn đề nhiều người dùng của 2+ người dùng chỉnh sửa cùng một khách hàng (vì vậy cuối cùng một để nhấn Cập nhật chiến thắng). Nhược điểm của PATCH là bạn phải theo dõi trên màn hình / chế độ xem của những gì đã thay đổi và có một số thông minh để chỉ gửi những phần đã thay đổi. Vấn đề nhiều người dùng của Patch được giới hạn ở việc chỉnh sửa (các) cột giống nhau của cùng một khách hàng.

0
harit 2020-10-13 21:07.

Một lời giải thích rất hay ở đây-

https://blog.segunolalive.com/posts/restful-api-design-%E2%80%94-put-vs-patch/#:~:text=RFC%205789,not%20required%20to%20be%20idempotent .

A Normal Payload- // Ngôi nhà trên lô 1 {địa chỉ: 'lô 1', chủ sở hữu: 'segun', type: 'duplex', color: 'green', phòng: '5', bếp: '1', cửa sổ: 20} PUT For Cập nhật- // PUT yêu cầu tải trọng để cập nhật các cửa sổ của Ngôi nhà trên lô 1 {address: 'lô 1', chủ sở hữu: 'segun', type: 'duplex', color: 'green', phòng: '5' , Kitchen: '1', windows: 21} Lưu ý: Trong tải trọng ở trên, chúng tôi đang cố gắng cập nhật các cửa sổ từ 20 lên 21.

Bây giờ hãy xem tải trọng PATH- // Tải trọng yêu cầu vá để cập nhật cửa sổ trên House {windows: 21}

Vì PATCH không phải là không cố định, các yêu cầu không thành công không tự động được thực hiện lại trên mạng. Ngoài ra, nếu một yêu cầu PATCH được thực hiện đến một url không tồn tại, ví dụ như cố gắng thay thế cửa trước của một tòa nhà không tồn tại, nó sẽ thất bại mà không cần tạo tài nguyên mới không giống như PUT, tài nguyên này sẽ tạo một tài nguyên mới bằng cách sử dụng tải trọng. Nghĩ lại, sẽ thật kỳ quặc khi có một cánh cửa duy nhất ở một địa chỉ nhà.

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Đây là Tổng thống Trump đe dọa 'trợ cấp' xe điện của GM vì đóng cửa nhà máy

Đây là Tổng thống Trump đe dọa 'trợ cấp' xe điện của GM vì đóng cửa nhà máy

Trump và Giám đốc điều hành GM Mary Barra vào năm 2017. Kể từ khi tin tức nổ ra ngày hôm qua rằng General Motors đang cắt giảm việc làm ở Bắc Mỹ để chuẩn bị cho tương lai điện / xe tự hành / khủng khiếp có thể xảy ra của chúng tôi, gần như tất cả mọi người đã tự hỏi: Tổng thống Trump, người đã vận động tranh cử trên một nền tảng sẽ như thế nào của việc mang lại và duy trì việc làm ô tô của Mỹ, phải nói gì về điều này? Bây giờ chúng ta biết.

Bản vá lỗi tiếp theo của Fallout 76 sẽ mang lại một số bản sửa lỗi cần thiết

Bản vá lỗi tiếp theo của Fallout 76 sẽ mang lại một số bản sửa lỗi cần thiết

Bethesda cho biết ngày hôm nay trên Reddit, Fallout 76 sẽ có một bản vá lớn khác vào ngày 4 tháng 12, sẽ bắt đầu cố gắng khắc phục một số vấn đề lớn hơn của trò chơi. Ngoài ra, công ty cho biết họ có kế hoạch giao tiếp nhiều hơn trong tương lai về những gì nhóm Fallout 76 đang làm việc và khi nào các bản cập nhật mới cho trò chơi sẽ ra mắt.

Mạng lưới bệnh viện Công giáo sẽ không cho phép nhân viên phá thai tại phòng khám ngoài giờ làm việc

Mạng lưới bệnh viện Công giáo sẽ không cho phép nhân viên phá thai tại phòng khám ngoài giờ làm việc

Bạn có biết rằng một trong sáu bệnh nhân ở bệnh viện ở Hoa Kỳ được điều trị tại một cơ sở Công giáo? Và bạn có biết rằng quyền sở hữu của Công giáo hạn chế sâu sắc việc thiết lập các thủ tục y tế có thể được thực hiện trong các bệnh viện này, rõ ràng nhất là khi nói đến chăm sóc sức khỏe sinh sản? Bây giờ NPR báo cáo rằng sự gia tăng của các bệnh viện thuộc Công giáo đang gây khó khăn hơn cho các bác sĩ — những người bị cấm bởi các quy định do Hoa Kỳ viết.

Khói từ đám cháy rừng ở California đã đến tận thành phố New York

Khói từ đám cháy rừng ở California đã đến tận thành phố New York

Đường chân trời San Francisco bị che khuất bởi khói và khói mù do cháy rừng phía sau Đảo Alcatraz, Thứ Tư, ngày 14 tháng 11 năm 2018, ở San Francisco.

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.

Language