since HTTP is designed to support optimisric locking (via etags) usually
this pattern is not needed.
but sometimes you still need to exhibit this workflow and what that comes
up, I use "Fielding Property Maps" pattern. It's worked very well in
several cases.
> What design patterns are best for checkin / checkout / abort in a REST
> interface?
> Jonathan
> --
> You received this message because you are subscribed to the Google Groups
> "API Craft" group.
> To unsubscribe from this group, send email to
> api-craft+unsubscribe@googlegroups.com.
> Visit this group at http://groups.google.com/group/api-craft?hl=en.
We definitely need optimistic locking, but we need pessimistic locking as well, including long term pessimistic locking where someone checks something out for hours or days.
Setting properties isn't quite what I'm looking for; ideally, I would like checkout to be one atomic operation that reads the object and gives exclusive checkin access.
On Wednesday, November 7, 2012 1:30:51 PM UTC-5, Mike Amundsen wrote:
> since HTTP is designed to support optimisric locking (via etags) usually > this pattern is not needed.
> but sometimes you still need to exhibit this workflow and what that comes > up, I use "Fielding Property Maps" pattern. It's worked very well in > several cases.
> On Wed, Nov 7, 2012 at 1:16 PM, Jonathan Robie <jonatha...@ibiblio.org<javascript:> > > wrote:
>> What design patterns are best for checkin / checkout / abort in a REST >> interface?
>> Jonathan
>> -- >> You received this message because you are subscribed to the Google Groups >> "API Craft" group. >> To unsubscribe from this group, send email to >> api-craft+...@googlegroups.com <javascript:>. >> Visit this group at http://groups.google.com/group/api-craft?hl=en.
> We definitely need optimistic locking, but we need pessimistic locking as
> well, including long term pessimistic locking where someone checks
> something out for hours or days.
> Setting properties isn't quite what I'm looking for; ideally, I would like
> checkout to be one atomic operation that reads the object and gives
> exclusive checkin access.
> Jonathan
> On Wednesday, November 7, 2012 1:30:51 PM UTC-5, Mike Amundsen wrote:
>> since HTTP is designed to support optimisric locking (via etags) usually
>> this pattern is not needed.
>> but sometimes you still need to exhibit this workflow and what that comes
>> up, I use "Fielding Property Maps" pattern. It's worked very well in
>> several cases.
>> On Wed, Nov 7, 2012 at 1:16 PM, Jonathan Robie
>> <jonatha...@ibiblio.org<javascript:>
>> > wrote:
>>> What design patterns are best for checkin / checkout / abort in a REST
>>> interface?
>>> Jonathan
>>> --
>>> You received this message because you are subscribed to the Google Groups
>>> "API Craft" group.
>>> To unsubscribe from this group, send email to
>>> api-craft+...@googlegroups.com <javascript:>.
>>> Visit this group at http://groups.google.com/group/api-craft?hl=en.
> --
> You received this message because you are subscribed to the Google Groups
> "API Craft" group.
> To unsubscribe from this group, send email to
> api-craft+unsubscribe@googlegroups.com.
> Visit this group at http://groups.google.com/group/api-craft?hl=en.
On Nov 7, 2012 4:37 PM, "Jonathan Robie"
<jonathan.robie<jonathan.ro...@ibiblio.org>
@ <jonathan.ro...@ibiblio.org>ibiblio.org <jonathan.ro...@ibiblio.org>>
wrote:
> We definitely need optimistic locking, but we need
> pessimistic locking as well, including long term
> pessimistic locking where someone checks
> something out for hours or days.
> Setting properties isn't quite what I'm looking for;
Would updating (via put) a "reserved_by" property of the resource using an
etag based optimistic lock solve the problem? The server could then reject
any modification requests from other users. This would seem to allow basic
checkout/in/abort. If these checkouts are multi-resource you'll need some
way to deal with deadlocks but it seems like that would be the case
regardless.
* Check out an object using a POST to the object's URI, with the URI parameter ?action=checkout, then read from the URI returned from the POST * Check in an object using a PUT to the URI used to read the object, with the URI parameter ?action=checkin * Abort a checkout using a POST to the URI used to read the object, with the URI parameter ?action=abort_checkout
On Wednesday, November 7, 2012 9:39:20 PM UTC-5, Peter Williams wrote:
> On Nov 7, 2012 4:37 PM, "Jonathan Robie" <jonathan.robie <javascript:>...@<javascript:> > ibiblio.org <javascript:>> wrote:
> > We definitely need optimistic locking, but we need > > pessimistic locking as well, including long term > > pessimistic locking where someone checks > > something out for hours or days.
> > Setting properties isn't quite what I'm looking for;
> Would updating (via put) a "reserved_by" property of the resource using an > etag based optimistic lock solve the problem? The server could then reject > any modification requests from other users. This would seem to allow basic > checkout/in/abort. If these checkouts are multi-resource you'll need some > way to deal with deadlocks but it seems like that would be the case > regardless.
I won't call it right or wrong, but having an "action" parameter is probably not what most API developers would consider RESTful, as there already is an action provided by the HTTP method.
Is it very important that the checkout and read object are an atomic operation? As soon as the object is locked, will atomicity be of great significance?
My suggestion is to introduce a sub-resource on the object that can be checked out or, in my words, locked? Assuming the resources are "books" and the subresource is "lock":
GET /books/42/lock -> 404 Not Found = not locked by anyone GET /books/42/lock -> 200 OK = locked by someone PUT /books/42/lock -> 403 Forbidden = already locked by someone else PUT /books/42/lock -> 200 OK = you locked it now (or refreshed an existing lock) DELETE /books/42/lock -> 403 Forbidden = you are not allowed to unlock it DELETE /books/42/lock -> 200 OK = you unlocked it
GET /books/42 -> 403 Forbidden = not locked GET /books/42 -> 200 OK = locked by you so you can read it PUT /books/42 -> 403 Forbidden = not locked PUT /books/42 -> 200 OK = locked by you so you can replace it DELETE /books/42 -> 403 Forbidden = not locked DELETE /books/42 -> 200 OK = locked by you so you can delete it
> * Check out an object using a POST to the object's URI, with the URI > parameter ?action=checkout, then read from the URI returned from the POST > * Check in an object using a PUT to the URI used to read the object, with > the URI parameter ?action=checkin > * Abort a checkout using a POST to the URI used to read the object, with > the URI parameter ?action=abort_checkout
> What's right or wrong with that design?
> Jonathan
> On Wednesday, November 7, 2012 9:39:20 PM UTC-5, Peter Williams wrote:
>> On Nov 7, 2012 4:37 PM, "Jonathan Robie" <jonathan.robie...@ibiblio.org> >> wrote:
>> > We definitely need optimistic locking, but we need >> > pessimistic locking as well, including long term >> > pessimistic locking where someone checks >> > something out for hours or days.
>> > Setting properties isn't quite what I'm looking for;
>> Would updating (via put) a "reserved_by" property of the resource using >> an etag based optimistic lock solve the problem? The server could then >> reject any modification requests from other users. This would seem to allow >> basic checkout/in/abort. If these checkouts are multi-resource you'll need >> some way to deal with deadlocks but it seems like that would be the case >> regardless.
Using links/hypermedia allows the client to be ignorant of the URL
structure. This means you can freely redesign your URL structure later on -
maybe even move the lock resources to a different server if needed - the
client won't care.
The client can then do a PUT to the lock resource to create the lock. When
it finishes it DELETEs the lock resource. Anyone can GET the lock resource
to see who is holding it right now.
You probably need some kind of authentication to keep track of who is
holding the lock (and thus be the one who can delete it, besides an
administrator).
/Jørn
On Thu, Nov 8, 2012 at 6:48 PM, Jonathan Robie
<jonathan.ro...@ibiblio.org>wrote:
> * Check out an object using a POST to the object's URI, with the URI
> parameter ?action=checkout, then read from the URI returned from the POST
> * Check in an object using a PUT to the URI used to read the object, with
> the URI parameter ?action=checkin
> * Abort a checkout using a POST to the URI used to read the object, with
> the URI parameter ?action=abort_checkout
> What's right or wrong with that design?
> Jonathan
> On Wednesday, November 7, 2012 9:39:20 PM UTC-5, Peter Williams wrote:
>> On Nov 7, 2012 4:37 PM, "Jonathan Robie" <jonathan.robie...@ibiblio.org>
>> wrote:
>> > We definitely need optimistic locking, but we need
>> > pessimistic locking as well, including long term
>> > pessimistic locking where someone checks
>> > something out for hours or days.
>> > Setting properties isn't quite what I'm looking for;
>> Would updating (via put) a "reserved_by" property of the resource using
>> an etag based optimistic lock solve the problem? The server could then
>> reject any modification requests from other users. This would seem to allow
>> basic checkout/in/abort. If these checkouts are multi-resource you'll need
>> some way to deal with deadlocks but it seems like that would be the case
>> regardless.
>> Peter
>> Barelyenough.org
> --
> You received this message because you are subscribed to the Google Groups
> "API Craft" group.
> To unsubscribe from this group, send email to
> api-craft+unsubscribe@googlegroups.com.
> Visit this group at http://groups.google.com/group/api-craft?hl=en.
On Thursday, November 8, 2012 4:22:14 PM UTC-5, David Eriksson wrote: > I won't call it right or wrong, but having an "action" parameter is > probably not what most API developers would consider RESTful, as there > already is an action provided by the HTTP method.
That makes sense, but when there are a large number of possible operations, if you're not using sub-resources, you may have a lot more actions than HTTP methods, e.g. copy, move, link, unlink, checkout, checkin, cancel checkout ...
> Is it very important that the checkout and read object are an atomic > operation? As soon as the object is locked, will atomicity be of great > significance?
> My suggestion is to introduce a sub-resource on the object that can be > checked out or, in my words, locked? Assuming the resources are "books" > and the subresource is "lock":
> GET /books/42/lock -> 404 Not Found = not locked by anyone > GET /books/42/lock -> 200 OK = locked by someone > PUT /books/42/lock -> 403 Forbidden = already locked by someone else > PUT /books/42/lock -> 200 OK = you locked it now (or refreshed an existing > lock) > DELETE /books/42/lock -> 403 Forbidden = you are not allowed to unlock it > DELETE /books/42/lock -> 200 OK = you unlocked it
> GET /books/42 -> 403 Forbidden = not locked > GET /books/42 -> 200 OK = locked by you so you can read it > PUT /books/42 -> 403 Forbidden = not locked > PUT /books/42 -> 200 OK = locked by you so you can replace it > DELETE /books/42 -> 403 Forbidden = not locked > DELETE /books/42 -> 200 OK = locked by you so you can delete it
That works well for checkout/checkin/cancel.
It's not clear to me how to use sub-resources as cleanly for copy, move, link, unlink - for instance, suppose I have a sub-resource for move, which moves a document from one collection to another. POST can be used to do a move operation, but what do these operations mean?
GET /books/42/move PUT /books/42/move DELETE /books/42/move
I've often wondered such things as well. My gut tells me there is no
imperative that every Http Verb must be supported by a resource. For
instance, if you just don't allow DELETE in the domain.
On Thu, Nov 8, 2012 at 4:05 PM, Jonathan Robie
<jonathan.ro...@ibiblio.org>wrote:
> On Thursday, November 8, 2012 4:22:14 PM UTC-5, David Eriksson wrote:
>> I won't call it right or wrong, but having an "action" parameter is
>> probably not what most API developers would consider RESTful, as there
>> already is an action provided by the HTTP method.
> That makes sense, but when there are a large number of possible
> operations, if you're not using sub-resources, you may have a lot more
> actions than HTTP methods, e.g. copy, move, link, unlink, checkout,
> checkin, cancel checkout ...
>> Is it very important that the checkout and read object are an atomic
>> operation? As soon as the object is locked, will atomicity be of great
>> significance?
>> My suggestion is to introduce a sub-resource on the object that can be
>> checked out or, in my words, locked? Assuming the resources are "books"
>> and the subresource is "lock":
>> GET /books/42/lock -> 404 Not Found = not locked by anyone
>> GET /books/42/lock -> 200 OK = locked by someone
>> PUT /books/42/lock -> 403 Forbidden = already locked by someone else
>> PUT /books/42/lock -> 200 OK = you locked it now (or refreshed an
>> existing lock)
>> DELETE /books/42/lock -> 403 Forbidden = you are not allowed to unlock it
>> DELETE /books/42/lock -> 200 OK = you unlocked it
>> GET /books/42 -> 403 Forbidden = not locked
>> GET /books/42 -> 200 OK = locked by you so you can read it
>> PUT /books/42 -> 403 Forbidden = not locked
>> PUT /books/42 -> 200 OK = locked by you so you can replace it
>> DELETE /books/42 -> 403 Forbidden = not locked
>> DELETE /books/42 -> 200 OK = locked by you so you can delete it
> That works well for checkout/checkin/cancel.
> It's not clear to me how to use sub-resources as cleanly for copy, move,
> link, unlink - for instance, suppose I have a sub-resource for move, which
> moves a document from one collection to another. POST can be used to do a
> move operation, but what do these operations mean?
> GET /books/42/move
> PUT /books/42/move
> DELETE /books/42/move
> Jonathan
> --
> You received this message because you are subscribed to the Google Groups
> "API Craft" group.
> To unsubscribe from this group, send email to
> api-craft+unsubscribe@googlegroups.com.
> Visit this group at http://groups.google.com/group/api-craft?hl=en.
OK, so one possibility is to raise errors for HTTP methods that don't make sense for a resource that represents an action, and model actions as sub-resources.
Here's a blog entry that criticizes this approach:
On Thursday, November 8, 2012 5:13:00 PM UTC-5, Kijana Woodard wrote:
> I've often wondered such things as well. My gut tells me there is no > imperative that every Http Verb must be supported by a resource. For > instance, if you just don't allow DELETE in the domain.
> On Thu, Nov 8, 2012 at 4:05 PM, Jonathan Robie <jonatha...@ibiblio.org<javascript:> > > wrote:
>> On Thursday, November 8, 2012 4:22:14 PM UTC-5, David Eriksson wrote:
>>> I won't call it right or wrong, but having an "action" parameter is >>> probably not what most API developers would consider RESTful, as there >>> already is an action provided by the HTTP method.
>> That makes sense, but when there are a large number of possible >> operations, if you're not using sub-resources, you may have a lot more >> actions than HTTP methods, e.g. copy, move, link, unlink, checkout, >> checkin, cancel checkout ...
>>> Is it very important that the checkout and read object are an atomic >>> operation? As soon as the object is locked, will atomicity be of great >>> significance?
>>> My suggestion is to introduce a sub-resource on the object that can be >>> checked out or, in my words, locked? Assuming the resources are "books" >>> and the subresource is "lock":
>>> GET /books/42/lock -> 404 Not Found = not locked by anyone >>> GET /books/42/lock -> 200 OK = locked by someone >>> PUT /books/42/lock -> 403 Forbidden = already locked by someone else >>> PUT /books/42/lock -> 200 OK = you locked it now (or refreshed an >>> existing lock) >>> DELETE /books/42/lock -> 403 Forbidden = you are not allowed to unlock >>> it >>> DELETE /books/42/lock -> 200 OK = you unlocked it
>>> GET /books/42 -> 403 Forbidden = not locked >>> GET /books/42 -> 200 OK = locked by you so you can read it >>> PUT /books/42 -> 403 Forbidden = not locked >>> PUT /books/42 -> 200 OK = locked by you so you can replace it >>> DELETE /books/42 -> 403 Forbidden = not locked >>> DELETE /books/42 -> 200 OK = locked by you so you can delete it
>> That works well for checkout/checkin/cancel.
>> It's not clear to me how to use sub-resources as cleanly for copy, move, >> link, unlink - for instance, suppose I have a sub-resource for move, which >> moves a document from one collection to another. POST can be used to do a >> move operation, but what do these operations mean?
>> GET /books/42/move >> PUT /books/42/move >> DELETE /books/42/move
>> Jonathan
>> -- >> You received this message because you are subscribed to the Google Groups >> "API Craft" group. >> To unsubscribe from this group, send email to >> api-craft+...@googlegroups.com <javascript:>. >> Visit this group at http://groups.google.com/group/api-craft?hl=en.
As other's have commented i prefer avoiding action uris when possible. They
are necessary and useful sometimes but i don't think this is one of those
situations. I also think having a separate lock resource for each resource
complicates things more than is necessary for most situations.
What about this, to lock a resource some user 24 will
==> GET /thingy/42
<-- 200 OK
ETag: 1234
{//data here
_links: {} // notice the lack of a reserved_by link
}
==> PUT /thingy/42
If-Match: 1234
{_links:{reserved_by: {href: "http://users/24"}}}
<-- 200 OK
ETag: 5678
{//data here
_links: {reserved_by: {href: "http://users/24"}}
}
Now user 24 "owns" thingy 42. If someone else attempts to update the thingy
42 they well get a 403 forbidden, hopefully with an explanation about how
user 24 has checked this resource out.
If the resource was modified by another client between the initial GET and
the lock acquisition the PUT will fail with a 412 precondition failed. The
client can then retry or fail as is appropriate for the use case.
To abort it would be as simple as
==> PUT /thingy/42
{_links: {reserved_by: null}}
<-- 200 OK
{// orig data here _links: {}}
Committing is similarly straight forward
==> PUT /thingy/42
{// updated data here
_links: {}} // notice no reserved_by link
<-- 200 OK
ETag: 1357
{// updated data here
_links: {}}
As a minor aside is the above approach of putting a null for the link
object an officially sanctioned way to delete a link in HAL?
I realize I was unclear previously. The "lock" subresource is the noun "lock", not the verb.
As Mike suggested previously in the thread you really should go through the WebDAV RFC: http://www.webdav.org/specs/rfc4918.html - It has LOCK, MOVE and COPY as HTTP methods.
If keeping regular HTTP methods, a MOVE could might be implemented with GET+PUT+DELETE, a COPY with GET+PUT, etc. Links could be implemented by using GET and PUT but having a separate media compared to the regular resource.
Further I must also admit that I am still quite stuck in the "URL construction" thinking. Peter and Jörn are more seasoned in thinking about this in hypermedia terms!
\David
Den torsdagen den 8:e november 2012 kl. 23:05:01 UTC+1 skrev Jonathan Robie:
> On Thursday, November 8, 2012 4:22:14 PM UTC-5, David Eriksson wrote:
>> I won't call it right or wrong, but having an "action" parameter is >> probably not what most API developers would consider RESTful, as there >> already is an action provided by the HTTP method.
> That makes sense, but when there are a large number of possible > operations, if you're not using sub-resources, you may have a lot more > actions than HTTP methods, e.g. copy, move, link, unlink, checkout, > checkin, cancel checkout ...
>> Is it very important that the checkout and read object are an atomic >> operation? As soon as the object is locked, will atomicity be of great >> significance?
>> My suggestion is to introduce a sub-resource on the object that can be >> checked out or, in my words, locked? Assuming the resources are "books" >> and the subresource is "lock":
>> GET /books/42/lock -> 404 Not Found = not locked by anyone
>> GET /books/42/lock -> 200 OK = locked by someone
>> PUT /books/42/lock -> 403 Forbidden = already locked by someone else
>> PUT /books/42/lock -> 200 OK = you locked it now (or refreshed an >> existing lock)
>> DELETE /books/42/lock -> 403 Forbidden = you are not allowed to unlock it
>> DELETE /books/42/lock -> 200 OK = you unlocked it
>> GET /books/42 -> 403 Forbidden = not locked
>> GET /books/42 -> 200 OK = locked by you so you can read it
>> PUT /books/42 -> 403 Forbidden = not locked
>> PUT /books/42 -> 200 OK = locked by you so you can replace it
>> DELETE /books/42 -> 403 Forbidden = not locked
>> DELETE /books/42 -> 200 OK = locked by you so you can delete it
> That works well for checkout/checkin/cancel.
> It's not clear to me how to use sub-resources as cleanly for copy, move, > link, unlink - for instance, suppose I have a sub-resource for move, which > moves a document from one collection to another. POST can be used to do a > move operation, but what do these operations mean?
> GET /books/42/move
> PUT /books/42/move
> DELETE /books/42/move
On Thursday, November 8, 2012 7:00:52 PM UTC-5, Peter Williams wrote:
> As other's have commented i prefer avoiding action uris when possible. > They are necessary and useful sometimes but i don't think this is one of > those situations. I also think having a separate lock resource for each > resource complicates things more than is necessary for most situations.
> What about this, to lock a resource some user 24 will
> ==> GET /thingy/42 > <-- 200 OK > ETag: 1234 > {//data here > _links: {} // notice the lack of a reserved_by link > }
> ==> PUT /thingy/42 > If-Match: 1234 > {_links:{reserved_by: {href: "http://users/24"}}} > <-- 200 OK > ETag: 5678 > {//data here > _links: {reserved_by: {href: "http://users/24"}} > }
> Now user 24 "owns" thingy 42. If someone else attempts to update the > thingy 42 they well get a 403 forbidden, hopefully with an explanation > about how user 24 has checked this resource out.
> If the resource was modified by another client between the initial GET and > the lock acquisition the PUT will fail with a 412 precondition failed. The > client can then retry or fail as is appropriate for the use case.
> To abort it would be as simple as
> ==> PUT /thingy/42 > {_links: {reserved_by: null}} > <-- 200 OK > {// orig data here _links: {}}
> Committing is similarly straight forward
> ==> PUT /thingy/42 > {// updated data here > _links: {}} // notice no reserved_by link > <-- 200 OK > ETag: 1357 > {// updated data here > _links: {}}
> As a minor aside is the above approach of putting a null for the link > object an officially sanctioned way to delete a link in HAL?
> Don't see how to use this design for copy/move/link/unlink, though. I'll
> start a separate thread on that.
> Jonathan
> On Thursday, November 8, 2012 7:00:52 PM UTC-5, Peter Williams wrote:
>> As other's have commented i prefer avoiding action uris when possible.
>> They are necessary and useful sometimes but i don't think this is one of
>> those situations. I also think having a separate lock resource for each
>> resource complicates things more than is necessary for most situations.
>> What about this, to lock a resource some user 24 will
>> ==> GET /thingy/42
>> <-- 200 OK
>> ETag: 1234
>> {//data here
>> _links: {} // notice the lack of a reserved_by link
>> }
>> ==> PUT /thingy/42
>> If-Match: 1234
>> {_links:{reserved_by: {href: "http://users/24"}}}
>> <-- 200 OK
>> ETag: 5678
>> {//data here
>> _links: {reserved_by: {href: "http://users/24"}}
>> }
>> Now user 24 "owns" thingy 42. If someone else attempts to update the
>> thingy 42 they well get a 403 forbidden, hopefully with an explanation
>> about how user 24 has checked this resource out.
>> If the resource was modified by another client between the initial GET
>> and the lock acquisition the PUT will fail with a 412 precondition failed.
>> The client can then retry or fail as is appropriate for the use case.
>> To abort it would be as simple as
>> ==> PUT /thingy/42
>> {_links: {reserved_by: null}}
>> <-- 200 OK
>> {// orig data here _links: {}}
>> Committing is similarly straight forward
>> ==> PUT /thingy/42
>> {// updated data here
>> _links: {}} // notice no reserved_by link
>> <-- 200 OK
>> ETag: 1357
>> {// updated data here
>> _links: {}}
>> As a minor aside is the above approach of putting a null for the link
>> object an officially sanctioned way to delete a link in HAL?
>> Peter
>> barelyenough.org
>> --
> You received this message because you are subscribed to the Google Groups
> "API Craft" group.
> To unsubscribe from this group, send email to
> api-craft+unsubscribe@googlegroups.com.
> Visit this group at http://groups.google.com/group/api-craft?hl=en.