Why Drupal 8 won't ship with REST content negotiation
Some friends on Twitter were alarmed by this Drupal change record:
Accept header based routing got replaced by a query parameter
curl /node/1 -H "Accept: application/hal_json"
The issues leading to this change are too lengthy to capture on Twitter, so I'll give my perspective here.
What was the problem with accept-routing?
- Most critically, not all CDNs and caches support content negotiation (see #2364011)
- A secondary issue that I think deserves more attention is that even when caches support content-negotiation, the cache hit rate suffers.
The poor hit rate arises because web browsers do not simply declare
Accept: text/html, they request a bizarre mishmash of media types depending on the specific browser, version, OS, and even what extensions are installed. A quick test on this site discovered roughly 100 unique Accept headers in 24 hours.
But isn't content-negotiation required to have "pure" REST?
- No. Roy Fielding has said "Neither choice has anything to do with REST" (referring to a debate between extensions or query parameters).
- While I don't deny the aesthetic appeal of the Accept header, there are good reasons why Drupal often has to de-prioritize theoretical purity. As mikl wrote on #2364011, "Drupal should be engineered for the web we have, rather than the one we wish we had."
?_format a Drupalism?
_format Routing Parameter comes from Symfony, so I think this would be more accurately called a Symfony-ism, if it's an -ism at all. However, given that REST is an architectural style, and not a specific protocol, any API we design will naturally contain some details unique to Drupal.
Can't Varnish normalize the wide variety of Accept headers?
Yes, but if custom normalization routines in Varnish were the only way to have a high-performing cache, then that would be a bad Drupal-ism. It's better to have broad compatibility with current CDNs and caches.
Could a contrib module re-enable content negotiation?
Probably. The Symfony route filters are plug-ins that can be swapped out.
What alternatives were considered?
Some other options were moving REST resources to an
/api path, or using extensions like
/node/1.json. In the end, the query parameter won out because it had the least impact on the existing routing system.
I think an ideal solution to both problems would be widespread support for the
Alternates: header (RFC 2295). In this way a server can specify what alternate media formats, languages, or encodings are available, and content negotiation can be efficiently performed at the network's edge. Sadly, this experimental RFC appears largely abandoned since its introduction in 1998, so perhaps the best we can hope for is by the time Drupal 9 is ready, CDNs and browsers will have cleaned up their act.