LƯU Ý : Khi tôi lần đầu tiên dành thời gian đọc về REST, idempotence 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ì những bình luận thêm (và câu trả lời của Jason Hoetger ) đã được hiển thị. 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 đạo văn Jason 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 các bình luận).
Sau khi đọc câu trả lời của tôi, tôi đề nghị bạn cũng đọc câu trả lời tuyệt vời 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 cần đánh cắp từ Jason.
Tại sao PUT idempotent?
Như bạn đã lưu ý trong trích dẫn RFC 2616 của bạn, PUT được coi là tạm thời. Khi bạn PUT một tài nguyên, hai giả định này đang diễn ra:
Bạn đang đề cập đến một thực thể, không phải là một bộ sưu tập.
Thực thể bạn đang cung cấp đã hoàn tất ( toàn bộ thực thể).
Hãy xem xét một trong những ví dụ của bạn.
{ "username": "skwee357", "email": "skwee357@domain.com" }
Nếu bạn POST tài liệu này /users
, như bạn đề xuất, thì bạn có thể lấy lại một thực thể như
## /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
Nếu bạn muốn sửa đổi thực thể này sau, bạn chọn giữa PUT và PATCH. Một PUT có thể trông như thế này:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // new email address
}
Bạn có thể thực hiện tương tự bằng cách sử dụng VÒI. Điều đó có thể trông như thế này:
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
Bạn sẽ nhận thấy một sự khác biệt ngay lập tức giữa hai. 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 thông số đã được sửa đổi ( email
).
Khi sử dụng PUT, giả định rằng bạn đang gửi thực thể hoàn chỉnh và thực thể hoàn chỉnh đó thay thế bất kỳ thực thể hiện có nào tại URI đó. Trong ví dụ trên, PUT và PATCH hoàn thành 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 phát hành cùng một yêu cầu, nó sẽ luôn có cùng 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à idempotent.
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 yêu cầu PUT?
GET /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
PUT /users/1
{
"email": "skwee357@gmail.com" // new email address
}
GET /users/1
{
"email": "skwee357@gmail.com" // 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 ... đó có thể không phải là trường hợp thực tế.)
Vì chúng tôi đã sử dụng PUT, nhưng chỉ được cung cấp email
, nên bây giờ đó là điều 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 - không bao giờ thực sự làm điều này. Yêu cầu PUT này là không có 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ệ, tồi tệ.
Làm thế nào để có thể bình tĩnh?
Trong ví dụ trên, PATCH là idempotent. Bạn đã thực hiện một thay đổi, nhưng nếu bạn thực hiện cùng một thay đổi lặp đi lặp lại, nó sẽ luôn trả lại 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": "skwee357@domain.com"
}
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
GET /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // email address was changed
}
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address... again
}
GET /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // nothing changed since last GET
}
Ví dụ ban đầu của tôi, cố định cho chính xác
Ban đầu tôi có những ví dụ mà tôi nghĩ là thể hiện sự không bình thường, nhưng chúng sai lệch / không chính xác. Tôi sẽ giữ các ví dụ, nhưng sử dụng chúng để minh họa một điều khác: đó là nhiều tài liệu PATCH chống lại 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 biểu tượng không phải là idempotent.
Hãy nói rằng tại một thời điểm trước đây, 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": "skwee357@olddomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
Sau một PATCH, bạn có một thực thể được sửa đổi:
PATCH /users/1
{"email": "skwee357@newdomain.com"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com", // 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 BỆNH NHÂN 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à idempotent.
Một giờ sau, sau khi bạn đi pha cà phê và nghỉ ngơi, một người khác đi cùng với BỆNH NHÂN của chính họ. Có vẻ như Bưu điện đã và đang thực hiện một số thay đổi.
PATCH /users/1
{"zip": "12345"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com", // still the new email you set
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345" // and this change as well
}
Vì cái 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à idempotent.
Ngày hôm sau, bạn quyết định gửi lại BỆNH NHÂN của mình.
PATCH /users/1
{"email": "skwee357@newdomain.com"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com",
"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ó đặt địa chỉ email. A đi vào, A đi ra, do đó đây là bình thường.
Những gì tôi đã sai trong câu trả lời ban đầu của tôi
Tôi muốn rút ra một sự khác biệt quan trọng (điều mà tôi đã sai trong câu trả lời ban đầu của mình). Nhiều máy chủ sẽ đáp ứng 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, 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 , bởi 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ỉ với email. Vì vậy, tài liệu PATCH của bạn vẫn còn trống - 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 bình thường thì sao?
Để điều trị đầy đủ cho câu hỏi này, một lần nữa tôi giới thiệu bạn với câu trả lời của Jason Hoetger . Tôi sẽ để 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 những gì anh ta đã có.