I'm trying to move a resource from /buckets/1
to /buckets/2
such that:
Initial state
- /buckets/1 = foo
- /buckets/2 = HTTP 404
Final state
- /buckets/1 = HTTP 301 to /buckets/2
- /buckets/2 = foo
What's a RESTful way of asking the server to move a resource in this manner?
Answering my own question:
- For the sake of this discussion let's assume that we are storing "balls" in buckets
- The first thing to notice is that a ball's life-cycle is not determined by its containing bucket (moving a ball from one bucket to another does not delete the old ball). As such we should promote balls to a top-level resource:
/balls
- REST seems to work best in terms of symbolic links as opposed to inline values, so instead of
GET /buckets/1
returning the value of the ball in the bucket let's have it return the URI of the ball instead.
We can then move balls as follows:
(examine original state)
GET /buckets/1: "balls = {'/balls/1'}"
GET /buckets/2: "balls = {}"
GET /balls/1: "bucket = /buckets/1"
(move ball into bucket #2)
PUT /balls/1: "bucket = /buckets/2"
(examine new state)
GET /buckets/1: "balls = {}"
GET /buckets/2: "balls = {'/balls/1'}"
GET /balls/1: "bucket = /buckets/2"
End-result: the ball's identity remains consistent as it moves across buckets and (most importantly) this operation is atomic.
GET /buckets/1
DELETE /buckets/1
PUT /buckets/2 {data returned by #1}
That doesn't make the server 301
, though. The alternative would be to use the WebDAV MOVE
method, i.e. by creating your own @MOVE
annotation using the @HttpMethod
annotation:
import ...;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("MOVE")
public @interface MOVE {}
but doing so breaks REST's architectural principle of using HTTP as a uniform interface (RESTful Java).