A RESTful chess service: part 5Author: Dave Cassel | Category: Software Development
The Typical Course of Events
There are two main use cases for this service. The first is a Tournament Director reporting the results of games. In this case, the TD will POST a PGN game to /games. The typical course here is that the service will translate the PGN to XML, use some of the fields to construct a URL, store the game, and returns its new URL. For this first version of the service, uploading a game is the only way to add data to the database. The other main use case is someone using the service to browse the contents of the database. Here, we expect that the consumer of the service will use a proper URL and the service will return a list of games that match. In the previous post, I worked out that the search results will be paginated.
As a secondary use case, the service allows a TD to upload a correction to a PGN record. This case leads to some interesting results. Naturally, if someone uses the service to retrieve an updated game, we want to show the updated version. The interesting part is what happens to the URL. Since we are constructing the game URLs from the PGN headers, updating a game record may well change the game’s URL. For instance, suppose that a TD POSTS a game with these PGN headers:
[Event "February 2010 Octet X"] [Site "http://redhotpawn.com"] [Date "2010.02.17"] [Round "1"] [White "David Cassel"] [Black "sagator"] [Result "1/2-1/2"]
The game will be accessible through the URL “/games/February+2010+Octet+X/http%3A%2F%2Fredhotpawn.com /2010.02.17/1/David+Cassel/sagator”. Later, the TD realizes that this site is usually recorded with the full address, including the “www”. The service will allow the TD to PUT the PGN with the corrected header ([Site “http://www.redhotpawn.com”]) to the URL created by the earlier POST. Two things happen as a result. First, the game will now be available at a URL based on the corrected headers: “/games/February+2010+Octet+X/%3A%2F%2Fredhotpawn.com/ 2010.02.17/1/David+Cassel/sagator”. But we also need to think about what to do if someone requests the original URL. After all, someone may have conducted a search or bookmarked the URL and may try to retrieve the game. HTTP code 301 Moved Permanently is the way to go here.
These seems like a good time to point out one of the simplifying assumptions I’ve made: that there’s no need to log in. In a real system, I would limit who is allowed to POST or PUT data, and might limit search and retrieval, depending on the intended use of the service. That means I’d need to think through how to handle security, how to model my users, how to respond to unauthorized access attempts, and so forth. MarkLogic Server provides handy ways to handle all that, but it’s not what I want to focus on right now. Maybe I’ll add that to the service in a future post.
Consider What Can Go Wrong
The steps listed by the book’s authors includes trying to anticipate what might go wrong while the service is being used. A little defensive programming can go a long way. So… what could possibly go wrong?
Let’s start with an obvious case: someone requesting a URL that doesn’t correspond to anything in the database. Is it worthwhile to list something basic like this? Personally, I think so. Listing such cases makes you decide whether you should return 404 Not Found, or simply an empty list with a 200 Success (you probably wouldn’t want this, but I’ve seen it happen). If someone asks the service for a game that isn’t in the database, the service will return a 404, but a search that has no results will get a 200 with an empty <results/> node. It also helps you to remember to address those cases in the code, so you don’t accidentally end up throwing a 500 Server Error.
Another potential error is a conflict — a TD POSTing a game with PGN fields that match those of a game already in the database. If this happens, we’ll return 409 Conflict, along with the URL of the conflicting game. That way, the TD will be able to look at the game that’s already in the database. It might be that the TD accidentally POSTed the same game twice, or it may be a mistake in one of the fields. By providing useful information, the consumer of the service will be able to figure out what went wrong.
Any time you expect a certain format for the data your users send you, you need to be prepared for the data not to match the format. In the chess service case, the PGN might be invalid, or it might be valid PGN that is missing some fields we require. We’ll treat these cases the same way, responding with a 400 Bad Request and a message stating the requirement of valid PGN and a list of required fields.
One way that I like to think of how things go wrong is to glance over the list of HTTP response codes. Sometimes it helps me think of a case that I might have overlooked otherwise. Based on a look at the list, I remembered that there are only a small number of permitted methods (GET on a particular game, event, player, or search; POST on /games; PUT on a particular game). Outside of those, we’ll return 405 Method Not Allowed.
I think that about covers it. I’ve now worked through the design for my RESTful chess service, informed in a couple places by some exploratory coding. Next time, we’ll look at how to implement the service with MarkLogic Server.
I found the procedure listed out by Richardson & Ruby to be a helpful one. Even though I’ve made some simplifying assumptions for this exercise, having a set of steps helped me go about the design in an organized way.
Do you have a particular approach you use to design a service?