9

I want to create an undo history whenever the user updates a form. Because it's an update, I want to use a PUT request. However, I read that PUT needs to have no side effects.

Is it acceptable to use PUT here? Are there better alternatives?

PUT /person/F02E395A235

{
   time: 1234567,
   fields: {
      name: 'John',
      age: '41'
   }
}

In the server

doPut('person/:personId',
   // create a new person snapshot
)

Edit:

The history will be visible to the user, calling multiple times would result in multiple versions.

The solution was to check if the version was unique before creating it.

10

The people drafting HTTP/2 were a lot more verbose about their ideas of what HTTP should do, while retaining the old meaning. Let's see what the HTTP/2 draft spec has to say about idempotence:

4.2.2. Idempotent Methods

A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request.Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.

Like the definition of safe, the idempotent property only applies to what has been requested by the user; a server is free to log each request separately, retain a revision control history, or implement other non-idempotent side-effects for each idempotent request.

The intended effect on the server for each such a PUT request is to update the resource identified by that URI. This is exactly what happens in your case.

That you decide to version resources doesn't really matter here. If you don't want to create a new version when nothing has changed, you'll need to compare the payload in the PUT request with the most recent (or otherwise identified) version of the resource, and when none of the properties have changed you can choose to not create a new version.


Your edit:

The history will be visible to the user, calling multiple times would result in multipleversions

As far as the resource is concerned, that is no side effect. The resource at that URI does not change (the same properties get PUT). History is just metadata, as it most probably is requested by a different URI or with different request headers.

  • Excepted because you answer my specific problem: "is it acceptable for PUT to create a user visible history", And give me a solution, thanks – roo2 Dec 5 '13 at 21:22
  • To what problem is that a solution? That the time property gets updated? I think that is metadata too, even though it is in the resource. – CodeCaster Dec 5 '13 at 21:54
  • 1
    The problem was that if multiple PUTs were sent, the user would get a long undo history with redundant information. checking for uniqueness solves that – roo2 Dec 6 '13 at 0:21
12

HTTP distinguishes between two properties:

  • Idempotency
  • Safety

Idempotency is defined by the spec as follows:

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request. The methods GET, HEAD, PUT and DELETE share this property. Also, the methods OPTIONS and TRACE SHOULD NOT have side effects, and so are inherently idempotent.

And safety:

In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". This allows user agents to represent other methods, such as POST, PUT and DELETE, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested.

Naturally, it is not possible to ensure that the server does not generate side-effects as a result of performing a GET request; in fact, some dynamic resources consider that a feature. The important distinction here is that the user did not request the side-effects, so therefore cannot be held accountable for them.

Note that safety implies idempotency: if a method has no side-effects then performing it multiple times will yield the same side-effect as performing it once, namely none.

This puts the methods into three categories:

  • safe (and thus also idempotent): GET, HEAD, OPTION, TRACE
  • idempotent but not necessarily safe: PUT, DELETE
  • neither idempotent nor safe: POST

PUT needs to have no side affects.

That is wrong. PUT is idempotent but not safe. The whole point of PUT is to have a side-effect, namely updating a resource. What idempotency means is that updating the same resource with the same contents multiple times should have the same effect as updating it only once.

Note the last paragraph in the section about safety [emphasis mine]:

Naturally, it is not possible to ensure that the server does not generate side-effects as a result of performing a GET request; in fact, some dynamic resources consider that a feature. The important distinction here is that the user did not request the side-effects, so therefore cannot be held accountable for them.

Although this sentence talks about GET and safety, we can assume that the authors also meant to apply the same reasoning to PUT and idempotency. IOW: PUT should have only one user-visible side-effect, namely updating the named resource. It may have other side-effects, but the user cannot be held responsible for them.

For example, the fact that PUT is idempotent means that I can retry it as often as I want: the spec guarantees that executing it multiple times will be exactly the same as executing it once. It's perfectly valid to create a backlog of old revisions as a side-effect of those multiple PUT requests. However, if, as a result of multiple retries, your database fills up with a backlog of old revisions, that is not my problem, it's yours.

IOW: you are allowed to have as many side-effects as you want, but

  1. it must look to the user as if their requests were idempotent
  2. you are responsible for those side-effects, not the user
  • Yep, idempotency is about the state of the resource being put, not about any other server/service state that is affected by the act of the PUT. – Marjan Venema Dec 5 '13 at 19:44
  • Upvote for great explanation, of safety and idempotency in Rest – roo2 Dec 5 '13 at 21:26
  • Great explanation. However, you make several statements like this: "The whole point of PUT is to have a side effect, namely updating a resource." That seems like a contradiction unless you mean something different by the term "side effect" than "something secondary or unintentional that happens in addition to the primary, intended effect." – MarredCheese Aug 22 at 4:00
  • @MarredCheese: I am using the term in its standard programming meaning, which basically means "any 'result' that is not the return value". – Jörg W Mittag Aug 22 at 9:33
  • Ah, of course. Thanks for the clarification. – MarredCheese Aug 22 at 13:56
1

You are correct that PUT needs to have no side affects, however I would add something to this.

PUT needs to have no side affects on the resource for which that PUT operation is being performed

You are updating a person resource which is identified as F02E395A235, so using PUT is correct. Now as a business rule you also keep track of changes which is invisible to the calling entity (consumer of the REST service). This will not add a new item in the person resource. The historical snapshot will not be accessible using the /person/ endpoint. So I believe, PUT should be perfectly acceptable in this case.

  • 1
    No side effects on the resource being put, but any number of side effects on other stuff (counters, logging, audit traces, ...) are perfectly acceptable. – Marjan Venema Dec 5 '13 at 19:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.