One of the questions that has arisen several times recently is validation using REST.
The scenario is that a client, a web page or some other agent, needs to do some quick validation for feedback to the user. For example, when entering a zipcode into a form we'd like to check that zipcode as it's entered and display some visual feedback when it's valid.
Let's follow our REST recipe and see what we end up with:
The noun in this case is a zipcode.
The representation will be a PNG file of a happy green checkmark.
The method will obviously be GET.
For status codes, if the given zipcode is not a valid
one then we will return a
404 Not Found, and a PNG of a
red X, otherwise we return a
200 Ok for the happy green
Note that we are returing small PNGs to make the web page easy to construct, and we are also sending back a 200 only on a good zipcode, which means that a client can programatically use this service by just checking the status code returned, ignoring the PNG.
The trick here is not caring about the size of the URI
space we are creating. The number of potential
is huge if we include all the 404's in there.
That is, the valid URIs in
are sparse. But since we are only keeping a list of
valid zipcodes we don't have to keep track
of the 404 space, only its complement.
That is, I know
/zipcode/foo isn't a valid
zipcode, but I don't have 'foo' in a file somewhere.
This is one example of a more general principal:
Tip: It's useful to think of URI space as infinite.
As specified in RFC 3986, URIs are not limited in length. In reality servers, browsers, and proxies have various limits that in practice bring the number down to 4096 characters. Let's do a quick back of the envelope calculation: if you just consider [a-zA-Z0-9] as a really stripped down set of characters allowed in a URI and then look at the longest practical URI, you are looking at roughly 62^4000 addressable resources. Just to put that in perspective a rough estimate for the number of atoms in the observable universe is 10^81.
URI space is infinite.
Here is the code for the lookup service. It presumes the existence of a file that contains all the valid zipcodes, sorted, with one per line. The file I used is very old and comes from the census bureau, so please don't use this service for anything legitimate; it's only presented for demonstration.
from mmap import mmap import os from bisect import bisect_left import sys class Zipcodes(object): """Use mmap to treat the sorted file of zipcodes as an array""" def __init__(self): self.f = open("sortzips.txt", "r+") self.size = os.path.getsize("sortzips.txt") self.m = mmap(self.f.fileno(), self.size) def __getitem__(self, i): self.m.seek(6*i) return self.m.read(5) def __del__(self): self.m.close() self.f.close() def __len__(self): return self.size / 6 zipcodes = Zipcodes() target = os.environ.get('PATH_INFO', '/')[1:] found = ( zipcodes[bisect_left(zipcodes, target)] == target ) print "Status: " + ( found and "200 Ok" or "404 Not Found" ) print "Cache-control: max-age=172800" print "Content-type: image/png" print "" f = open(found and "good.png" or "bad.png", "r") png = f.read() f.close() sys.stdout.write(png)