I wanted the webservices to be as RESTful as possible, so they should use the Accept:
header rather than file name extensions to determine the type. As I noted before, this won't work in general, because most browsers don't send the correct Accept:
headers. Still, that's no reason not to use the header if no extension is sent.
Parsing the header is straightforward; the mime-types are separated by commas (note that the top-level separation is the comma, not the semicolon) and within each mime-type description the parameters are separated by semicolons. The first field is the mime-type itself (with possible wild card '*'), the second (if present) is the quality factor, "q={number from 0 to 1}" which describes how much the requestor wants that particular mime-type, and the remainder of the fields are other parameters.
As the specification says, parameters in the Accept:
header are rare, and my programs don't use them, so we will ignore them. That means that Accept: text/html; q=0.8; level=2, text/html; q=0.2; level=1
is interpreted the same as Accept: text/html; q=0.2
; not exactly the same as the spec but easier to code.
The q parameter technically should be weighted by how much the host wants to send that type. Thus, Accept: image/jpeg; q=1, image/gif; q=0.2
means "Send me a jpeg
; I'll accept
a gif
, but I'll be only 20% as happy." If the host feels that the gif
is 10 times better than the jpeg
, it should rank the gif
as 10*0.2 = 2 and the jpeg
as 1*1 = 1, and send the gif
. My code doesn't have such strong feelings and ignores such niceties.
A q parameter of 0 should be interpreted as "never send me this type" but my code simply ignores it and, if there are no other mime-types in the Accept:
header, sends the default. If it's the default type that has the q=0, then the requestor loses.
One subtle trick from Kris Jordan's Recess: wild card ('text/*' and '*/*') types should rank below more specific types with the same q parameter. We subtract 0.0001 from the stated q to push them lower; the specification says the value cannot have more than 3 decimal places.
function parseAccept ($accept){
$mimetypes = array( // associate types with file extensions
'*/*' => 'html',
'text/*' => 'txt',
'text/plain' => 'txt',
'text/html' => 'html',
'text/csv' => 'csv',
'text/javascript' => 'js',
'image/*' => 'png',
'image/png' => 'png',
'image/gif' => 'gif',
'image/jpeg' => 'jpg',
'application/*' => 'js',
'application/json' => 'js',
'application/xml' => 'xml'
);
$types = array();
foreach (explode(',', $accept) as $mediaRange){
@list ($type, $qparam) = preg_split ('/\s*;\s*/', $mediaRange); // the q parameter must be the first one according to the RFC
$q = substr ($qparam, 0, 2) == 'q=' ? floatval(substr($qparam,2)) : 1;
if ($q <= 0) continue;
if (substr($type, -1) == '*') $q -= 0.0001;
if (@$type[0] == '*') $q -= 0.0001;
$types[$type] = $q;
}
arsort ($types); // sort from highest to lowest q value
foreach ($types as $type => $q){
if (isset ($mimetypes[$type])) return $mimetypes[$type];
}
return 'html';
}
The same sort of code would work for Accept-Encoding:
and the other Accept-type headers.
Parsing the HTTP Accept: header says:
[…] http://bililite.nfshost.com/blog/2010/01/06/parsing-the-http-accept-header/ […]
December 30, 2011, 11:53 pmYouTalkTech says:
Accept-Encoding http header variations deviations normalize gzip, deflate, identity…
i was quite surprised about the problem that comes with the http accept-encoding header when using a reverse proxy or http accelorator. you might have the following response header from your backend webservers: Vary: Accept-Encoding which is telling th…
June 13, 2012, 3:33 am