I'm working on RestWS integration for the Backbone module (#1463024: Build RestWS Flavor), and have run into an issue regarding opposite uses of PUT and POST in Backbone.js's save() method as compared to the RestWS module's behavior, as defined in the README.txt.
While RestWS uses HTTP PUT /<entity type name>
for create and HTTP POST /<entity type name>/<entity id>.<format>
for update, Backbone uses HTTP POST /<entity type name>
for create (though the create URL can be changed...the REST verb is harder modify, though) and HTTP PUT /<entity type name>/<entity id>.<format>
for update.
I think there is considerable reason to believe that Backbone's implementation is the "correct" one with regard to the HTTP 1.1 standard and the intended use, though there is some variation in opinion that could warrant a flexible interpretation (perhaps an admin setting to switch?)
For reference, see the W3's definition of POST vs. PUT, where the contrast is drawn that:
The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.
By this definition the POST URI should not include the entity ID, as it should refer to the entity type handler or other gateway, while the PUT URI can (must?) include the ID.
As this blog post discusses, PUT and POST are not defined in terms of CRUD operations, and there are understandable interpretations in which either could be used for create or update, but only if two successive calls to the PUT create would result in the same object, as PUT is defined in terms of its idempotence. Because subsequent calls to create a new node will result in nodes with different nid's, however, our PUT / is not idempotent.
I should note that while I have some experience with REST development and JSON REST frameworks, I cannot consider myself an expert. Still, I think these documents make a pretty strong case for POST for create and PUT for update in RestWS's case.
I am also very excited about the potential of using RestWS's entity-based approach with Backbone. What is the best way to resolve this issue?
Thanks for everything,
Ethan
Comment | File | Size | Author |
---|---|---|---|
#9 | swap_put_post2.patch | 5.8 KB | sepgil |
#8 | swap_put_post.patch | 4.43 KB | sepgil |
Comments
Comment #1
ethanw CreditAttribution: ethanw commentedComment #2
klausiHm, I'm not sure where we pulled the information for PUT == create and POST == update, but there was a source somewhere.
Anyway, we cannot just switch the behavior in the 1.x branch as this could break lots of clients. What we could do is to allow PUT and POST for create and update. So the semantics would be shifted to the presence of an id. If there is an id then it must be an update, if it is missing it must be a create. This contradicts your quotation from above a bit, but since everything is so fuzzy undefined anyway we could get away with it.
Comment #3
ethanw CreditAttribution: ethanw commentedI think allowing both makes sense. Perhaps it could be a variable setting, with the default for new installations being POST for create and PUT for update, but the update hook could set to the opposite for existing installs?
Comment #4
Robin Millette CreditAttribution: Robin Millette commentedI have a feeling that an option defaulting to current behaviour would be preferable to something automagically hard-coded in the long run.
Comment #5
klausiSo we have several options to solve this:
1) We just change the meaning of PUT and POST and do nothing else, which will break existing sites (they cannot update the module)
2) We create a RESTWS 2.x branch where we correct this. The 1.x branch will be frozen for old sites and will get security updates only.
3) We introduce a variable setting as proposed above that indicates which is PUT and which is POST.
4) We don't really care about POST/PUT, we just assume that a request with an id is an update and a request without is a create operation.
I think I like option 2 best. What do you think?
Comment #6
setvik CreditAttribution: setvik commentedAnother option would be a hybrid of #2 and #3.
i.e. execute option #3 in the 1.x branch and include help text for the option on the admin settings screen that indicates the option will be deprecated in 2.x and that the module will standardize on post=create and put=update. Defaulting new installs to post=create/put=update and existing installs to the reverse will prevent existing sites from breaking as Ethan suggested above. Could maybe even have a drupal_set_message in there for admin users of existing installs that links to the settings page and encourages them to start migrating their code in preparation.
It's a bit more work but would allow new users to confidently start using the module and would provide existing users ample opportunity and warning to convert their code.
That said, it may be enough to go with just option #2 and include a clear warning on the d.o module page about post/put.
I'd be happy to help implement option #3 if you guys decide to include it.
Comment #7
klausiI see no reason to implement the variable setting if we are starting a new branch anyway. We should get this right in the 2.x branch and do an alpha release immediately afterwards.
BTW: currently RESTWS has no admin settings screen, so we would have to create that for the setting. Meh.
Comment #8
sepgil CreditAttribution: sepgil commentedHere is a patch that swaps put and create.
Comment #9
sepgil CreditAttribution: sepgil commentedForgot to adapt the changes to the readme, so here is a new patch.
Comment #10
klausiThanks, committed to the new 7.x-2.x branch. I have also updated the project page and added a dev snapshot.
Please test this, I'm planning to release the first alpha of 2.x on Monday.
Comment #12
cmjns CreditAttribution: cmjns commentedSorry to resurrect this, but I'd like to weigh in with an additional consideration, re breaking client code with changes to API semantics. I've been wishing for an API that gets much closer to a real Hypermedia API rather than just a sort-of-RESTful CRUD API. To this end, I think a significant step forward for RestWS would be to make use of rel attributes that link out to a resource description that communicates to clients what the intended semantics are. So,
would indicate to the client that node creation can happen at /node. Since rel is an URI it also can be followed by the client to get information about how to perform the intended state transition. /node/create could be used to tell the client that a POST with such and such data will create a node, including which fields are required, etc. This information can be cached by the client so that they don't have to hit the URL every time.
Doing this allows you to change all manner of characteristics of the API without breaking client code, so long as clients actually are using the API correctly (looking at rel and not making assumptions about semantics and URL structure).
Comment #13
cmjns CreditAttribution: cmjns commentedSorry to resurrect this, but I'd like to weigh in with an additional consideration, re breaking client code with changes to API semantics. I've been wishing for an API that gets much closer to a real Hypermedia API rather than just a sort-of-RESTful CRUD API. To this end, I think a significant step forward for RestWS would be to make use of rel attributes that link out to a resource description that communicates to clients what the intended semantics are. So,
would indicate to the client that node creation can happen at /node. Since rel is an URI it also can be followed by the client to get information about how to perform the intended state transition. /node/create could be used to tell the client that a POST with such and such data will create a node, including which fields are required, etc. This information can be cached by the client so that they don't have to hit the URL every time.
Doing this allows you to change all manner of characteristics of the API without breaking client code, so long as clients actually are using the API correctly (looking at rel and not making assumptions about semantics and URL structure).
Comment #14
Crell CreditAttribution: Crell commentedcmjns: That's a valid feature request, but should be a new request as it's a separate issue.
Comment #15
cmjns CreditAttribution: cmjns commentedYep. I should know better. http://drupal.org/node/1683792
Comment #16
ethanw CreditAttribution: ethanw commentedConfirming that this fix addresses the issues in using with Backbone. Backbone module now supports RestWS out of the box: http://drupal.org/node/1463024#comment-6453512
Comment #16.0
ethanw CreditAttribution: ethanw commentedCorrected format of issue link #