Problem/Motivation
In #2445723: Use the $request format instead of the ContentNegotation., we removed Accept header negotiation. That's all well and good for the cases where the client, like a browser, accepts multiple formats, making negotiation difficult. However, in the case of an HTTP API (e.g., JSON:API), a client might specify a single format in the Accept header. In such cases, we know enough to set the request format early in the processing, and should do so to both solve for certain edge-case bugs and optimize retrieval from page cache.
By HEAD not currently honoring an unambiguous Accept header, we have the following bug:
- Perform jQuery.ajax('/node/1', {'headers': {'accept': 'application/json'}})
. The client is explicitly asking for JSON and only for JSON. /node/1
is not available in JSON, so the correct response is to return a 406. HEAD currently ignores the Accept header and returns a 200, with the body in HTML format.
Additionally, by HEAD not determining the format from an unambiguous Accept header, modules like JSON:API are adding their own middleware to do it. For example, see #3027980: [upstream] Move FormatSetter from middleware to a route filter, removing conditionality on Accept header. Except, they're doing it wrong, because they're only checking for the presence of the media type in the Accept header, not that it's the only one there.
Proposed resolution
Expand NegotiationMiddleware::getContentType()
to return a format that can be unambiguously determined from the Accept header.