Why SnipURL’s API is Unsafe a.k.a. How NOT to design your Web API
January 26th, 2008Sigh.
If you’ve read my blog, you know that I like well designed URLs. You’ve probably also discerned that I like web APIs with well designed URLs. And further you might be aware that I like APIs that are as lightweight as possible that use; i.e. ones that use PUT and/or POST content of mime-type “application/x-www-url-encoded” and that respond with a body in of the “text/html” or “application/json” mime-types. So on the surface you might think that I’d really like the SnipURL API; after all it is the epitome of simple.
But unfortunately, I don’t like SnipURLs API. You see, it violates one of the most important tenants of HTTP which is “GET requests MUST be ’safe’; i.e. a GET should have “no side effects.” SnipURL allows programs to use HTTP GET to issue a request to add a URL to their database of “snips.”
buy cheap cialis
purchase paxil
online xenical
cheap celexa
buy clomid
order acomplia
generic nexium
For example, to “snip” the URL “http://blog.welldesignedurls.org/url-design/” and enable the URL “http://snipr.com/urldesign” to redirect to it simply type the following URL into your browser (your browser will issues a “GET” request to SnipURL’s server API):
http://snipr.com/site/snip?r=simple
&link=http://blog.welldesignedurls.org/url-design/
&title=URL+Design
&snipnick=urldesign
This GET could also have been issued by a programming language, which of course is the whole point of an API. Unfortunately, the fact that the SnipURL server adds a record to their “snip” database on a GET request is a side effect and violates the HTTP spec. But before you think I’m just being a pedantic standardista with my undershorts on too tight, take a gander at the firestorm that resulted when Google released it’s Google Web Accelerator in early 2005. GWA caught lots of web developers with their pants down, including the golden boys of Rails who evidently hadn’t read the HTTP specs either (to their credit, they’ve made great strides since.)
So when I realized their API was violating the spec I of courseemailed their Google group with comments very similar to the ones I left on Douglas Karr’s blog where I first learned about SnipURL in hopes they could nip any damage in the bud. I’d point you to my email on the list but unfortunately they moderated my comments. On the other hand they did reply back to me in email. They responded with the following otherwise cordial email that was filled with mistaken assumptions:
Hello,
Thanks for an informative post.
Snipurl’s API is based on requests from our own users who wished to use our API, who stated clearly that they would prefer not to have to go through the rigmarole of charting through XML return values, and instead preferred a simple snip returned to them.
As for the comment: “GET is used for safe interactions and SHOULD NOT have the significance of taking an action other than retrieval.”
This is exactly what Snipurl’s API does. While we “get” the form values, they are rigorously checked for validity (which is in our own interest; otherwise miscreants could ruin our system) and only a value is returned.
The POST method is also significantly more resource-intensive when you talk of non-trivial traffic. Because we are a free service, we chose our approach based on practical reasons rather than puritanical design goals.
Again, it is great to have some feedback from the community. If I can find the time, I’ll be doing a REST API sometime, as has been on the cards anyway.
Many thanks
SnipURL Editor
So first off it is clear from their reply that their knowledge of HTTP is limited and that they are making assumptions that don’t follow from the comments I sent them. But I’m not posting their email to attack them or make fun of them, I’m posting it to illustrate that many professional web developers today don’t know the rules of HTTP, probably more than half. Instead I’m using their email as an example in hopes to educate those who, like me for the first 10+ years of my web development career, just let their tools isolate them from HTTP (yes I’m talking to you Visual Studio, IIS, and ASP/ASP.NET.)
So let me address their points one at a time.
First they said their users wished to use an API without having to “go through the rigmarole of charting through XML return values.” Obviously they were reading my email in haste and assuming that I was advocating XML when I most definitely not. And it is all the more ironic that they assumed XML advocacy given my recent anti-XML rant on the rest-discuss list. To be clear, I didn’t suggest the use of XML but what I did suggest (using PUT or POST) is no harder than programming a GET.
Second they claimed that their “rigorously check for validity” made their GET “safe” demonstrates a fundamental ignorance of what the term “safe” means related to HTTP GET. The term has a very precise meaning according to the spec, and for those that don’t know it I highly recommend they read “Safe Interations” from the W3C Technical Architecture Group’s Finding “URIs, Addressability, and the use of HTTP GET and POST.” In addition the checklist explaining when to use GET vs POST from the same document is both short and highly readable so web developers who’ve never learned the HTTP spec in detail should at least read that.
Lastly they claimed that “the POST method is significantly more resource-intensive than non-trivial traffic” (when compared to GET) means they really do not understand GET or POST. Let’s look at the difference between the two; the first is a GET and the second is a POST (NOTE: I’ve wrapped the HTTP requests to keep from overflowing the blog’s borders but you would not wrapped them in an HTTP request. The wrapped lines start with “>>>”):
GET http://snipr.com/site/snip?r=simple
>>> &link=http://blog.welldesignedurls.org/url-design/
>>> &title=URL+Design
>>> &snipnick=urldesign
>>> HTTP/1.1
POST http://snipr.com/site/snip HTTP/1.1
<blank line goes here>
r=simple
>>> &link=http://blog.welldesignedurls.org/url-design/
>>> &title=URL+Design
>>> &snipnick=urldesign
So where’s the resource intensiveness of the latter? The latter actually transmits fewer characters over the wire (not that a few characters make any difference except at “Yahoo-scale.”) What’s more, the overhead of writing the snip to their database will be orders of magnitude more overhead than the difference between GET vs. POST (if there even is any difference), though their may be some trivial differences on some server frameworks.
But better than a POST, I’d recommend a PUT. Note how I PUT to the URL that we want to be our snip URL thus eliminating the awkward “snipnick” parameter (this PUT’s body is wrapped too):
PUT http://snipr.com/urldesign HTTP/1.1
<blank line goes here>
r=simple
>>> &link=http://blog.welldesignedurls.org/url-design/
>>> &title=URL+Design
So hopefully the SnipURL Editor and anyone else reading this will now realize that it is important to ensure that APIs always use HTTP GETs in a ‘safe‘ manner. Of course if they don’t “get” what I’m trying to say (pun intended), then maybe I should just post the following code to a page on my website (or something similar for other HTTP-violating APIs) and let Google’s spiders have at it. :-)
<html> <title>Spidering SnipURL's Naughty API</title> <body> <h1>Spidering SnipURL's Naughty API...</h1> <dl> <?php $url= "http://snipr.com/site/snip?r=simple" . "&link=http://www.w3.org/2001/tag/doc/whenToUseGet?nick=<<nick>>” . “&title=HTTP+GETs+MUST+be+SAFE” . “&snipnick=”; for($i=0; $i<=36; $i++) { $nick = $_GET[’nick’] . chr( ($i<26 ? 97 : 22)+$i ); $nick_url = str_replace(’<<nick>>’,$nick,$url) . $nick; print ‘<dt><a href=”?nick=’ . $nick . ‘”>’ . “$nick</a></dt>”; print ‘<dd><a target=”_blank” href=”‘ . $nick_url. ‘”>HTTP GETs MUST be SAFE</a></dd>’; } ?> </dl> </body> </html>
Whadayathink? Should I post it to my site? ….. Nah, I’ll be nice today.
P.S. After writing this post it’s occurring to me that maybe SnipURL simply thought I was suggesting they use a SOAP API instead? Clearly all their rationales would apply against SOAP as:
- SOAP uses XML,
- SOAP “safety” implies validation (I think), and
- SOAP is a LOT more overhead than using a GET or a PUT/POST.
Think maybe that’s what SnipURL thought I meant?
