Keep in mind I have a rudimentary understanding of REST. Let\'s say I have this URL:
http://api.animals.com/v1/dogs/1/
And now, I want to m
I answered earlier, but this answer contradicts my old answer and follows a much different strategy for coming to a solution. It shows how the HTTP request is built from the concepts that define REST and HTTP. It also uses PATCH
instead of POST
or PUT
.
It goes through the REST constraints, then the components of HTTP, then a possible solution.
REST is a set of constraints intended to be applied to a distributed hypermedia system in order to make it scalable. Even to make sense of it in the context of remotely controlling an action, you have to think of remotely controlling an action as a part of a distributed hypermedia system -- a part of a system for discovering, viewing, and modifying interconnected information. If that's more trouble than it's worth, then it's probably no good to try to make it RESTful. If you just want a "control panel" type GUI on the client that can trigger actions on the server via port 80, then you probably want a simple RPC interface like JSON-RPC via HTTP requests/responses or a WebSocket.
But REST is a fascinating way of thinking and the example in the question happens to be easy to model with a RESTful interface, so let's take on the challenge for fun and for education.
REST is defined by four interface constraints:
identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.
You ask how you can define an interface, meeting these constraints, via which one computer tells another computer to make a dog bark. Specifically, you want your interface to be HTTP, and you don't want to discard the features that make HTTP RESTful when used as intended.
Let's begin with the first constraint: resource identification.
Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on.
So a dog is a resource. It needs to be identified.
More precisely, a resource R is a temporally varying membership function MR(t), which for time t maps to a set of entities, or values, which are equivalent. The values in the set may be resource representations and/or resource identifiers.
You model a dog by taking a set of identifiers and representations and saying they are all associated with each other at a given time. For now, let's use the identifier "dog #1". That brings us to the second and third constraints: resource representation and self-description.
REST components perform actions on a resource by using a representation to capture the current or intended state of that resource and transferring that representation between components. A representation is a sequence of bytes, plus representation metadata to describe those bytes.
Following is a sequence of bytes capturing the intended state of the dog, i.e. the representation we wish to be associated with the identifier "dog #1" (note that it only represents part of the state as it does not regard the dog's name, health, or even past barks):
It has been barking every 10 minutes since the time this state change was effected, and will continue indefinitely.
It is supposed to be attached to metadata that describes it. This metadata might be useful:
It is an English statement. It describes part of the intended state. If it is received multiple times, only allow the first to have an effect.
Finally, let's look at the fourth constraint: HATEOAS.
REST ... views an application as a cohesive structure of information and control alternatives through which a user can perform a desired task. For example, looking-up a word in an on-line dictionary is one application, as is touring through a virtual museum, or reviewing a set of class notes to study for an exam. ... The next control state of an application resides in the representation of the first requested resource, so obtaining that first representation is a priority. ... The model application is therefore an engine that moves from one state to the next by examining and choosing from among the alternative state transitions in the current set of representations.
In a RESTful interface, the client receives a resource representation in order to figure out how it should receive or send a representation. There must be a representation somewhere in the application from which the client can figure out how to receive or send all representations it should be able to receive or send, even if it follows a chain of representations to arrive at that information. This seems simple enough:
The client asks for a representation of a resource identified as the homepage; in response, it gets a representation that contains an identifier of every dog the client might want. The client extracts an identifier from it and asks the service how it can interact with the identified dog, and the service says the client can send an English statement describing part of the intended state of the dog. Then the client sends such a statement and receives a success message or an error message.
HTTP implements REST constraints as follows:
resource identification: URI
resource representation: entity-body
self-description: method or status code, headers, and possibly parts of the entity-body (e.g. the URI of an XML schema)
HATEOAS: hyperlinks
You've decided on http://api.animals.com/v1/dogs/1
as the URI. Let's assume the client got this from some page on the site.
Let's use this entity-body (the value of next
is a timestamp; a value of 0
means 'when this request is received'):
{"barks": {"next": 0, "frequency": 10}}
Now we need a method. PATCH fits the "part of the intended state" description we decided on:
The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI.
And some headers:
To indicate the language of the entity-body: Content-Type: application/json
To make sure it only happens once: If-Unmodified-Since: <date/time this was first sent>
And we have a request:
PATCH /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
Content-Type: application/json
If-Unmodified-Since: <date/time this was first sent>
[other headers]
{"barks": {"next": 0, "frequency": 10}}
On success, the client should receive a 204 status code in response, or a 205
if the representation of /v1/dogs/1/
has changed to reflect the new barking schedule.
On failure, it should receive a 403
and a helpful message why.
It is not essential to REST for the service to reflect the bark schedule in a representation in response to GET /v1/dogs/1/
, but it would make the most sense if a JSON representation included this:
"barks": {
"previous": [x_1, x_2, ..., x_n],
"next": x_n,
"frequency": 10
}
Treat the cron job as an implementation detail that the server hides from the interface. That's the beauty of a generic interface. The client doesn't have to know what the server does behind the scenes; all it cares about is that the service understands and responds to requested state changes.
If we assume Barking is an inner / dependent / sub resource which the consumer can act on, then we could say:
POST http://api.animals.com/v1/dogs/1/bark
dog number 1 barks
GET http://api.animals.com/v1/dogs/1/bark
returns the last bark timestamp
DELETE http://api.animals.com/v1/dogs/1/bark
doesn't apply! so ignore it.