It is more and more common to receive requests about APIs development. In the web world, APIs are usually accessed via the HTTP protocol and return data in JSON, XML or even HTML format.
First of all: what do I do with an API, developing a website? I can expose methods for remote access (just think about a mobile application, or an admin amministration panel). Secondly, I can easily load data asyncronously (for example to save / load a forum post without having to refresh the page).
But how should I develop them? Is there a hidden pattern or something like that to correctly build them?
:::HTTP POST /api/article/new POST /api/article/67/edit GET /api/article/67/delete
... and so on so forth. You could think that could be enought. And that may be true, if you don't need complex APIs, but on the negative side, the developer who access your API may have to learn tons of URLs to interface his program with your API. And that programmer may be yourself, after creating n-different possible URLs and relative methods. What about this way?
:::HTTP POST /api/article PUT /api/article/67 DELETE /api/article/67
... or even (but more cryptic) ...
:::HTTP POST /api/article PUT /api/article DELETE /api/article/67
Isn't it more intuitive? What? Do even PUT and DELETE methods exist? Hell yeah! The HTTP protocol allows you to call web pages with the following (standard) methods:
- GET (data is encoded and put directly in the URL)
- POST (data is being formatted and put into the HTTP content part of the packet(s))
- PUT (same as POST, but used to REPLACE a content)
- DELETE (no data is being sent but the URL, used to delete a content)
- (other methods I won't describe as not data-related)
Note that in PHP there is no
$_PUT variable. Instead you can use the
php://input data stream.
To sort things up, if we want to get or manipulate the article with ID 67, we'll just need to do the following:
:::HTTP POST /api/article # to create a new article (and maybe get the ID) GET /api/article/67 # to get the article PUT /api/article/67 # to update the article DELETE /api/article/67 # to delete the article
Just one URL "and a half" to handle an entire article with semantic HTTP calls. Isn't this just clean and simple to remember? You may still be doubtful objecting that you could use just POST to do everything, in the following way:
:::HTTP POST /api/article/new POST /api/article/update POST /api/article/get POST /api/article/delete
I'd say first of all that you're just ignoring what the HTTP protocol is offering you just for those purposes, and second that you're re-defining something already defined in a standard (the HTTP protocol itself!). Think about an API in a language different from English: the keywords (new, update, get, delete) may be different, forcing the developer to RTFM, because the API is not "rock solid".
So, our API structure looks amazing, but in which language should I return the result? Well, here is where things become really complicated. Facing the problem of creating an API for the first time, you'd probably don't even ask yourself this question, outputting things directly in XML or JSON. And you should probably do just that. But if your API target is someone who knows a little bit of networking, it would be nice to let the API caller choose.
I usually create APIs that output JSON, JSONP, XML and HTML with little to no effort but the HTML part (see the last paragraph about this).
The first approach to "select" the result's language would be to append it somewhere in the URL or in the data fields. But that's not stylish enought for us!
HTTP provides an interesting, linear and logic solution to our problem: the use of the
Accept field of the HTTP request's header part. The header is defined as following:
:::HTTP Accept: media-range[; accept-params]
Accept header tells the web server in which language the client would like to receive the response.
media-range is the MIME type. The asterisk allows you to not specify a particular kind of MIME type, for example
*/* means "I accept everything", while
audio/* means "I accept any audio format" and
application/json means "I accept JSON".
accept-params are optional and tell the server the option(s) about the specified
media-range. The only option we need is "q" and stands for "quality factor". This is a value between 0 and 1 (default: 1) which tells the server how much the MIME type is wanted.
:::HTTP Accept: application/json; q=1 # is equal to Accept: application/json
Obviously, the quality factor is useless here (as shown in the second part of the example).
We could even request the server a page allowing different languages, if the preferred one is not available:
:::HTTP Accept: application/json, application/xml; q=0.5, application/EDI-X12; q=0.1
This means "Hello server, I'd like to get the response in JSON (I really like it!). If you can't, please send me the XML version (I'm not very happy). If you still can't do that, send me the response in EDI-X12, but I'd be very unhappy about that".
In this way you could elaborate the request in a PHP class, returning a "raw" array, encoded in the right way reading the
Accept HTTP header field: so the browser, which prefers
- 15th June 2013