Clients can use GET and HEAD to retrieve representations of built-in places, their own places (whether public or private), and public places created by others. Clients can delete their own places with DELETE, and change the state of their places with PUT. There are two ways a client might create a map annotation. The client might add a comment to an existing place on the map (“Mount Rushmore”), or it might give a new name to a certain point of latitude and longitude (“the cornfield where I kissed Betty”). In the first case, the resource being created is “Mount Rushmore (from leonardr’s point of view).” When creating this resource the client shouldn’t have to know exactly where on the map Mount Rushmore is. “Mount Rushmore” is a consensus name and there’s a built-in place by that name. The client can rely on the server to look up the coordi- nates. In the second case, the resource being created is a brand new place that the server’s never heard of, and the client is responsible for knowing the coordinates. How can I work this feature into my resource-oriented design? “Mount Rushmore (from leonardr’s point of view)” is a subordinate resource of another resource: the built- in place “Mount Rushmore.” This resource already exists and has a URI: one of them
is /Earth/Mount%20Rushmore. If the client wants to reuse the consensus name for a place, it shouldn’t have to look up its location. Instead of figuring out the final URI of the annotation and sending a PUT request to it, the client can send a POST request to the “Mount Rushmore” URI and let the server figure out the ultimate URI.
Similarly, if the client wants to comment on the Alabama capitol building, it can POST to /Earth/USA/AL/State%20capitol instead of figuring out the exact coordinates or street address. Any URI that identifies a built-in place can be the target of a POST request that comments on that place.
What about custom names? What if a client wants to give the name “Mount Rushmore” not to the original in South Dakota, but to the scale model in Imaichi? What if the client wants to create an annotation for “the cornfield where I kissed Betty”?
Here the client must know the latitude and longitude of the place it wants to create. This means it’ll have all the information necessary to create the URI of the new resource: the world, a geographic point on the world, the name of the place, and its own user- name. The client could make a PUT request to a URI like /user/bob/Earth/42;-93.7/ the%20cornfield%20where.... This would work just like creating a user account by sending a PUT request to /user/bob.
Even here, it’s cleaner to use POST. A brand-new place on the map is a subordinate resource: it’s subordinate to some point on the planet, just like a comment on a built- in place is subordinate to a place on the planet. So a client could also put a new place on the map by sending a POST request to /Earth/42;-93.7. It works the same way as a comment on existing places (a POST to /Earth/Mount%20Rushmore), except here the place is identified by latitude and longitude, not by consensus name.
My service will support POST for brand-new places because that’s simpler. The inter- face will be the same whether you’re adding a brand new place to the planet, or making a comment on some consensus place. Another service might support both methods: PUT to the final URI if the client is willing to figure out that URI, and POST to a parent URI if it’s not.
Finally, note that although I’m using POST, it’s not overloaded POST. Clients of my service use POST only when they want to create a resource “beneath” an existing one. The URI of the new resource (/user/leonardr/Earth/43.9;-103.46/Mount%20Rushmore) may not directly extend the URI of the old (/Earth/Mount%20Rushmore), but the resources have a conceptual relationship.
Design the Representation(s) Accepted from the Client
When the client sticks a pin into a planet and creates a custom place, what information does it need to provide? It must identify a planet and a place on that planet: the spot where the pin goes. The place can be identified either by latitude and longitude, or by reference to a canonical name like “Mount Rushmore.” Call these variables planet, latitude, longitude, and name. The server must know what type of place the client is
putting on the map. A place may be public or not, and the client may provide a custom description of the place. The final URI also incorporates a username, but the client is already providing that, in the Authorization header. There’s no need to make the client send that information twice.
These are all key-value pairs. I can have clients represent places the way they represent user accounts: as form-encoded strings. There are no complex data structures here that might call for a JSON or XML representation.
Client requests may choose to send some key-value pairs and omit others. Information that’s in the URI as scoping information doesn’t need to be repeated in the represen- tation. When the client sends a POST to /Earth/43.9;-103.46 it doesn’t need to specify latitude and longitude, because that information’s in the URI. It does need to specify name and type.
When the client sends a POST to /Earth/Mount%20Rushmore it shouldn’t specify latitude, longitude, or name. The client is making a new place based on a well-known existing place, and the new place will inherit the name and location of the existing place. The client may specify a custom type (“national-park,” “political,” “places in North Dakota”) or inherit the default (“monument”).
The client may always choose to omit description and public. My service sets default values for those variables: descriptions are empty by default, and places are public by default.
When the client modifies one of its custom places, anything and everything about the place might change: its name, its location, its type, its description, or its public status. The PUT request that modifies a place can specify the same key-value pairs used to create a place, in any combination. The server will make the appropriate changes, as- suming the changes make sense.
Example 6-6 shows a sample HTTP POST request that creates a new custom place. Combined, the form-encoded representation and the scoping information in the URI convey all required states for the new resource. The name and location of the new resource come from the scoping information; its type and description come from the representation. Since the representation doesn’t specify a value for public, the default takes over and this new resource is made public.
Example 6-6. An HTTP request that creates a subordinate resource
POST /Earth/USA/Mount%20Rushmore HTTP/1.1 Host: maps.example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=