Most of the work here is already done. In Chapter 5 I defined an XHTML-based rep- resentation format for places. Custom places look the same as places from the built-in database.
The only new part is this: when an authenticated client requests a representation of one of its custom places, our service will tack onto the representation some hypermedia showing the client how to edit that place (see Example 6-7). I don’t need to tell clients how to delete the place: the uniform interface takes care of that. But I do need to convey the information I wrote in prose above: that a place is defined by planet, latitude, longitude, and so on.
Example 6-7. A hypermedia form showing the client how to edit one of its places
<form id="modifyPlace" method="PUT" action=""> <p>Modify this place:</p>
<p>
Name: <input name="name" value="Mount Rushmore" type="text" /><br /> Type: <input name="type" value="national-park" type="text" /><br /> Position:
<input name="latitude" value="43.9" type="text" />,
<input name="longitude" value="-103.46" type="text" /><br /> Description:
<textarea name="description">We visited on 3/5/2005</textarea><br /> Public?
<input name="public" type="checkbox" value="on"/> <input type="submit" />
</p> </form>
The caveats from earlier apply here too. This isn’t valid XHTML 4, though it is valid XHTML 5, because it specifies PUT as its method. Also, a client doesn’t know what to do with this form unless it’s been programmed in advance. Computers don’t know what “modifyPlace” means or what data might be a good value for “latitude.” Because clients have to be programmed in advance to understand these forms, most of today’s services don’t include a form for modifying a resource in that resource’s rep- resentation. They either serve all the forms up front (in a WSDL or WADL file), or they specify them in prose (as I did above) and leave it for the service programmer to figure out. It’s debatable whether it’s really helpful to serve forms along with representations, but serving them is better than just specifying the API in prose and making the pro- grammer implement it.
Link This Resource to Existing Resources
I’ve got three kinds of integration to do. The first is data integration. When you DE- LETE a user account, the account’s custom places—everything under /user/{user
name}—should also be deleted. URIs to these resources used to work, but now they will return a response code of 410 (“Gone”) or 404 (“Not Found”).
The other kinds of integration should be familiar by now. They involve changing the representations of existing resources to talk about the new one. I want search results to link to custom places. I want points on the globe to show how the user can create a custom place at that point. I want to improve my connectedness by connecting “custom place” resources to the resources I defined already.
The rather empty-looking representation of a user’s account, seen in Example 6-3, badly needs some link-based integration. This is the ideal place to list a user’s custom places. I’ll represent the place list with the same XHTML list of links I use to represent search results.
In the service defined in Chapter 5, a client that searched for places called “Mount Rushmore” (/Earth?show=Mount+Rushmore) would only find places from my built-in place database: probably only the “consensus” location of Mount Rushmore in South Dakota. In the new version of the service, there’s likely to be more than one result. In the new version, that search will also return other users’ annotations for Mount Rush- more, and other places that users have named “Mount Rushmore,” like the scale model in Imaichi.
This is the same case as in Chapter 5, where the built-in place database contained more than one “Joe’s diner.” I present search results in a list, each linking to a specific re- source. All I’m doing is expanding the search. A search result may be a place in the built-in database, a custom place created by some other user and exposed publicly, or a custom place created by the authenticated user (which may be public or private). I also need to show the client how to create its own places on the map. Custom places are created as subordinate resources of existing places. The logical thing to do is to put that information in the representations of those places: places with URIs like /Earth/ Mount%20Rushmore and /Earth/42;-93.7.
Example 6-8 is a possible representation of /Earth/43.9;-103.46 that brings together most of what I’ve covered in the past two chapters. This representation abounds in hypermedia. It links to a certain point on several different maps, a place from the built- in database, custom places from other users, and a custom place created by the au- thenticated user. It also has a hypermedia form that will let the authenticated user create a new custom place at these coordinates. Compare this representation to the smaller representation of /Earth/43.9;-103.46 back in Example 5-9.
Example 6-8. An XHTML representation of 43.9N 103.46W on Earth
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head>
<title>43.9°N 103.46°W on Earth</title> </head>
<body>
<p class="authenticated"> You are currently logged in as
<a class="user" href="/user/leonardr">leonardr</a>. </p>
<p> Welcome to
<a class="coordinates" href="/Earth/43.9,-103.46">43.9°N 103.46°W</a>
on scenic <a class="place" href="/Earth">Earth</a>. </p>
<p>See this location on a map:</p> <ul class="maps">
<li><a class="map" href="/road/Earth/43.9;-103.46">Road</a></li>
<li><a class="map" href="/satellite/Earth/43.9;-103.46">Satellite</a></li> ...
</ul>
<p>Places at this location:</p> <ul class="places">
<li>
<a class="builtin" href="Mount%20Rushmore">Mount Rushmore</a> System data says:
<span class="description">The world's largest sculpture</span> </li>
<li>
<a class="custom" href="Mt.%20Rushmore/user1">Mt. Rushmore</a> <a class="user" href="/users/user1">user1</a> says:
<span class="description">Built on land stolen from the Lakota tribe</span> </li>
<li>
<a class="custom" href="Mount%20Rushmore%20Gift%20Shop/user2"> Mount Rushmore Gift Shop
</a>
<a class="user" href="/users/user1">user1</a> says: <span class="description">Best fudge I've ever had</span> </li>
<li>
<a class="custom-private" href="Mount%20Rushmore/leonardr">Mt. Rushmore</a> You said: <span class="description">We visited on 3/5/2005</span>
</li> </ul>
<form id="searchPlace" method="get" action=""> <p>
Show nearby places, features, or businesses:
<input name="show" repeat="template" /> <input class="submit" />
</p> </form>
<form id="createPlace" method="post" action=""> <p>Create a new place here:</p>
<p>
Name: <input name="name" value="" type="text" /><br /> Type: <input name="type" value="" type="text" /><br /> Description:
<textarea name="description"></textarea><br /> Public?
<input name="public" type="checkbox" value="on"/> <input type="submit" />
</p> </form> </body> </html>
What’s Supposed to Happen?
This new resource, the custom place, mostly works like other resources I’ve already defined. A custom place responds to GET just like a built-in place. It responds to PUT (with a representation consisting of key-value pairs) and DELETE (with no represen- tation) just like “user account” resources do. I only have a couple new edge cases to consider here.
When the client creates a custom place, the response code is 201 (“Created”). This works the same way as users. But it was never possible to cause a user’s URI to change, because I prohibited users from changing their usernames. It’s possible to change the name of a place, or to move one (say, a ship) from one point on the map to another. Either of these actions will change the URI.
When the client modifies a custom place without changing its location, the response code will be 200 (“OK”). If the location changes, the response code will be 301 (“Moved Permanently”) and the Location header will contain the place’s new URI. The client is responsible for updating its data structures to keep track of the new URI. This ties into a debate I’ll revisit in Chapter 8, about whether it’s more important to have URIs that contain useful information, or URIs that never change. My URIs describe a custom place using two pieces of resource state: coordinates and name (/user/leonardr/Earth/ 43.9;-103.46/Mt.%20Rushmore). If either of those changes, the old URI breaks.
Broken URIs are no fun on the human web, and they’re even less fun on the program- mable web. If my custom “place” is a ship or something else that’s constantly moving, it effectively has no permanent URI. This is the single biggest design flaw in my system. If I were exposing this as a real web service, I’d probably give a “permalink” to every place: an alternate URI that doesn’t incorporate any changeable resource state. Since everything about a place can change except the planet it’s on and the person who owns
it, these URIs will not look very friendly: my annotation of Mount Rushmore might be accessible from /user/leonardr/Earth/36028efa8. But at least they’ll always refer to the same place.
What Might Go Wrong?
This new kind of resource introduces new error conditions, but most of them are var- iations of ones I’ve already covered, so I’ll pass over them quickly. The client might try to move an existing place off of the map by providing an invalid latitude or longitude: the response code is 400 (“Bad Request”), just as it was in a similar case in Chapter 5. The 400 response code is also appropriate when a client tries to create a place without providing all the information the server needs. This is similar to the 400 response code the server sends if the client tells the server to change a user’s password, but doesn’t actually provide the new password.
My service doesn’t allow a single user to define more than one place with the same name at the same coordinates. /user/leonardr/Earth/43.9;-103.46/Mt.%20Rushmore can only identify one place at a time. Suppose a client has two places called “My car,” and makes a PUT request that would move one to the location of the other. My service rejects this request with a response code of 409 (“Conflict”). There’s nothing wrong with moving a place to a certain set of coordinates; it’s just that right now there happens to be another place with that name there. The same 409 response code would happen if the client had two custom places at the same coordinates, and tried to rename one to match the name of the other. In either case, the client is making a syntactically valid request that would put the system’s resources into an inconsistent state. It’s the same as trying to create a user that already exists.
There’s one totally new error condition worthy of attention: the client may try to access a private place created by someone else. There are two possibilities. The first is to deny access with response code 403 (“Forbidden”). The 403 response code is used when the client provides no authentication, or insufficient authentication; the latter certainly applies in this case.
But a response code of 403 is a tacit admission that the resource exists. The server should not be giving out this information. If client A creates a custom place and marks it private, client B should not be able to figure out anything about it, even its name, even by guessing. When revealing the existence of a resource would compromise se- curity, the HTTP standard allows the server to lie, and send a response code of 404 (“Not Found”).