opengeospatial / ogcapi-common

OGC API - Common provides those elements shared by most or all of the OGC API standards to ensure consistency across the family.
https://ogcapi.ogc.org/common
Other
47 stars 14 forks source link

Be more specific about Accept header and recommend "Vary: Accept" #277

Open pomakis opened 3 years ago

pomakis commented 3 years ago

Section 8.7 ("Resource Encodings") of "OGC API - Common - Part 1: Core" states:

"A Web API provides access to resources through representations of those resources. One property of a representation is the format used to encode it for transfer. Components negotiate the encoding format to use through the content negotiation process defined in IETF RFC 7231."

This statement should probably be more specific by referring specifically to section 5.3 of IETF RFC 7231. It also might be a good idea to give an example HTTP Accept request header to make it clear that this form of content negotiation is encouraged.

In addition to this, it should also be recommended that any OGC API resource whose representation can be negotiated via the HTTP Accept header should include a "Vary: Accept" response header. This tells the client to take multiple representations into account when caching. This recommendation could be made either in section 8.7 or in section 8.4 ("Web Caching"); I'm note sure where it'd be more appropriate.

pomakis commented 3 years ago

Here's a real-life example of the need for the "Vary: Accept" response header. The {landingPage}/api endpoint of the CubeWerx OGC API server can serve two representations: "application/json" and "text/html". If requested directly by the Google Chrome browser, the following Accept header is automatically sent:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

and as a result the user gets an HTML page as expected. If the request is made without an Accept header, or with an Accept header that explicitly requests "application/json" as a preferred representation, the JSON OpenAPI document is returned. Our HTML representation of this resource is a simple HTML page which makes an Ajax request to fetch the JSON representation so that it can be displayed all pretty-like using SwaggerUI. So far so good. But here's where things went wrong.

The JSON representation was being requested with the following Accept header:

Accept: application/json,*/*

which (presumably due to indicating "withCredentials: true" and therefore being more careful with the cache) always correctly fetched the JSON representation. But then sometimes later on, when the user would request the HTML representation again directly via the web browser, the raw JSON OpenAPI representation would be displayed. It turns out that it was because the web browser wasn't aware that the "Accept" header would make any difference, and decided to return the JSON representation that was cached by the earlier Ajax request.

Adding "Vary: Accept" to our responses resolved this. (This is exactly the purpose of the Vary header.) To be thorough, we also added a vendor-specific "f=json" query parameter to the URL of the Ajax request, but that shouldn't be necessary.

cportele commented 3 years ago

Note that the same applies to Accept-Encoding and Accept-Language, too. If the resource supports multiple encodings or languages, these need to be added to the Vary header, too.

cmheazel commented 3 years ago

We already have nineteen recommendations and permissions in Part 1, none of which are testable. Rather than further dilute the Standard, I would like to capture this information in the Users Guide. Hyperlinks in the Standard would point users to this additional information everywhere it's appropriate.