I'm fairly familiar with REST principles, and have read the relevant dissertation, Wikipedia entry, a bunch of blog posts and StackOverflow questions on the subject, but still haven't found a straightforward answer to a common case:
I need to request a resource to display. Depending on the resource's state, I need to render either a read-only or an editable representation. In both cases, I need to GET the resource. How do I construct a URL to get the read-only or editable version?
If my user follows a link to GET /resource/<id>
, that should suffice to indicate to me that s/he needs the read-only representation. But if I need to server up an editable form, what does that URL look like? GET /resource/<id>/edit
is obvious, but it contains a verb in the URL. Changing that to GE开发者_开发问答T /resource/<id>/editable
solves that problem, but at a seemingly superficial level. Is that all there is to it -- change verbs to adjectives?
If instead I use POST to retrieve the editable version, then how do I distinguish between the POST that initially retrieves it, vs the POST that saves it? My (weak) excuse for using POST would be that retrieving an editable version would cause a change of state on the server: locking the resource. But that only holds if my requirements are to implement such a lock, which is not always the case. PUT fails for the same reason, plus PUT is not enabled by default on the Web servers I'm running, so there are practical reasons not to use it (and DELETE).
Note that even in the editable state, I haven't made any changes yet; presumably when I submit the resource to the Web server again, I'd POST it. But to get something that I can later POST, the server has to first serve up a particular representation.
I guess another approach would be to have separate resources at the collection level:
GET /read-only/resource/<id>
and GET /editable/resource/<id>
or GET /resource/read-only/<id>
and GET /resource/editable/<id>
... but that looks pretty ugly to me.
Thoughts?
1) It is perfectly valid to have two distinct resources, one for viewing and one for editing some domain concept. Just be aware that because they are two different URIs from REST's perspective they are two different resources. Too often people conflate resource with domain object. That's why they end up being stuck only doing CRUD.
2) Don't get too hung up on the name of the resource. The important thing is that you realize that what the URI points to is a "thing", "a resource". If that's more obvious to you with editable
instead of edit
then use that. Having a verb in your URL doesn't make your application wrong, it just makes it a bit less readable to the human developer. Using a verb in the URL to try and redefine the semantics of the HTTP method, now that's a violation of the uniform interface constraint.
In REST, editing an existing resource is accomplished by a client GET-ing a representation of that resource, making changes to the representation, and then doing a PUT of the new representation back to the server.
So to just read a resource your REST client program would do a:
GET http://www.example.com/SomeResource
And to edit that resource:
GET http://www.example.com/SomeResource
... edit it ...
PUT http://www.example.com/SomeResource
Normally simultaneous updates are handled by letting the last PUT arriving at the server overwrite the earlier ones, on the assumption that it represents a newer state. But in your case you want to guard against this.
Carefully consider @Jason's suggestion to maintain an optional parallel lock resource for each main resource. Your client would first create the lock, do the edit, then delete the lock. Your system would need to release a lock automatically if the user making the lock subsequently never saves any changes. This would look like:
GET http://www.example.com/SomeResource
... user presses an edit button ...
PUT http://www.example.com/SomeResource/lock
... user edits the resource's representation ...
PUT http://www.example.com/SomeResource
DELETE http://www.example.com/SomeResource/lock
You'd need to do some appropriate error handling if the user is trying to edit a resource that's locked by someone else.
It sounds like you feel you're constrained by the current limitations of HTML. If you use a server-side REST framework like Restlet (for Java), it supports the notion of "overloaded POST", where you can use POST but tack on a query string argument like method=PUT or method=DELETE. If you're writing your own server-side components they can use this trick too.
There are tricks you can play at the HTML level too. For instance your page can have a read-only part that's initially displayed, and an input form that's initially not shown. When the user presses the edit button, your JavaScript hides the read-only part and shows the input form.
Be sure to read Richardson and Ruby's Restful Web Services (O'Reilly) too. It's extremely helpful.
I don't think returning a form or just values is up to a REST server, but the responsibility of the client. Whether a resource is editable is a property of the resource, and not something defined by the URL.
In other words: The URL for getting the resource is GET /resource/<id>
. This has a property editable. If a user wants a form it can retrieve the resource from the same URL and populate the form. The client can than PUT/POST changes.
How do I construct a URL to get the read-only or editable version?
There's an underlying problem here, which is that you are constructing URLs in the first place - appending IDs to hard-coded URLs is not REST. Roy Fielding has written about this very mistake. Whichever document prompts you to edit the resource should contain the URI to the editable variant of that resource. You follow that URI, whether that's /resource/editable
or /editable/resource
is outside the scope of REST.
If instead I use POST to retrieve the editable version, then how do I distinguish between the POST that initially retrieves it, vs the POST that saves it?
You perform a GET (not a POST) to read the resource, and POST (or PUT) to write the resource.
If you want to create a lock on the resource in question, use POST to write to the resource (or the resource's container, with the resource ID encoded in the body of the POST), and have the server create a lock as a new resource, and return an ID of that resource as the response to the POST. (with authentication issues beyond the scope of your question or this answer)
Then to unlock the lock, either use a DELETE on the lock resource, or POST to the lock's container.
I guess your question could be "how to identify the readonly representation that return with GET action in PUT action?". You could do this:
<Root>
<readonly>
<p1><p1>
...
<readonly>
<others>
...
<others>
<Root>
After parsing the request XML from PUT you can ignore the readonly part and process others. In Response, return 200 status and leave a message saying the part in readonly is ignored. Is it your expected?
精彩评论