Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does HTTP content negotiation respect media type parameters

An HTTP request can include an Accept header, indicating the media type(s) of responses that the client will find acceptable. The server should honour the request by providing a response that has a Content-Type that matches (one of) the requested media type(s). A media type may include parameters. Does HTTP require that this process of content-negotiation respect parameters?

That is, if the client requests

 Accept: application/vnd.example; version=2

(here the version parameter has a value of 2), and the server can serve media-type application/vnd.example; version=1, but not application/vnd.example; version=2, is it OK for the server to provide a response with

 Content-Type: application/vnd.example; version=1

Is it OK for the server to provide a response labelled

 Content-Type: application/vnd.example; version=2

but for the body of the response to actually be encoded as media-type application/vnd.example; version=1? That is, for the parameters of the media-type of a response to be an inaccurate description of the body of the response?

It seems that Spring MVC 4.1.0 does not respect media-type parameters when doing content negotiation, and gives responses for which the parameters of the media-type of the response are an inaccurate description of the body of the response. This seems to be because the org.springframework.util.MimeType.isCompatibleWith(MimeType) method does not examine the parameters of the MimeType objects.

like image 230
Raedwald Avatar asked Aug 18 '15 11:08

Raedwald


2 Answers

The relevant standard, RFC 7231 section 3.1.1.1, says the following about media-types:

The type/subtype MAY be followed by parameters in the form of name=value pairs.

So, Accept and Content-Type headers may contain media type parameters. It adds:

The presence or absence of a parameter might be significant to the processing of a media-type, depending on its definition within the media type registry.

That suggests that the server code that uses parameter types should pay attention to them, and not simply discard them, because for some media types they will be significant. It has to implement some smarts in whether to consider whether the media type parameters are significant.

Spring MVC 4.1.0 therefore seems to be wrong to completely ignore the parameters when doing content negotiation: the class org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor is incorrect to use org.springframework.util.MimeType.isCompatibleWith(MimeType), or that MimeType.isCompatibleWith(MimeType) method is incorrect. If you provide Spring with several HTTP message converters that differ only in the parameters of their supported media type, Spring will not reliably choose the HTTP message converter that has the media type that exactly matches the requested media type.


In section 3.1.1.5, where it describes the Content-Type header, it says:

The indicated media type defines both the data format and how that data is intended to be processed by a recipient

As the parameters of a media type in general could vary the data format, the behaviour of Spring MVC 4.1.0 is wrong, in providing parameters that are an inaccurate description of the body of the response: the method AbstractMessageConverterMethodProcessor.getMostSpecificMediaType(MediaType, MediaType) is wrong to return the acceptType rather than the produceTypeToUse when the two types are equally specific.


However, section 3.4.1, which discusses content negotiation (Proactive Negotiation), notes:

A user agent cannot rely on proactive negotiation preferences being consistently honored, since the origin server might not implement proactive negotiation for the requested resource or might decide that sending a response that doesn't conform to the user agent's preferences is better than sending a 406 (Not Acceptable) response.

So the server is permitted to give a response that does not exactly match the media-type parameters requested, as a fall-back when it can not provide an exact match. That is, it may choose to respond with a application/vnd.example; version=1 response body, with a Content-Type: application/vnd.example; version=1 header, despite the request saying Accept: application/vnd.example; version=2, if, and only if generating a valid application/vnd.example; version=2 response would be impossible.


This apparently incorrect behaviour of Spring already has a Spring bug report, SPR-10903. The Spring developers closed it as "Works as Designed", noting

I don't know any rule for comparing media types with their parameters effectively. It really depends on the media type...If you're actually trying to achieve REST versioning through media types, it seems that the most common solution is to use different media types, since their format obviously changed between versions:

  • "application/vnd.spring.foo.v1+json"
  • "application/vnd.spring.foo.v2+json"
like image 169
Raedwald Avatar answered Oct 11 '22 22:10

Raedwald


The relevant spec for content negotiation in HTTP/1.1 is RFC2616, Section 14.1.

It contains the following example, relevant to your question:

Accept: text/*, text/html, text/html;level=1, */*

and gives the precedence as

1) text/html;level=1
2) text/html
3) text/*
4) */*

So I think it is safe to say that text/html;level=1 and text/html are different media types. I would also consider text/html;level=1 and text/html;level=2 as different.

So in your example I think it would be correct to respond with a 406 error and not respond with a different media type.

like image 26
wero Avatar answered Oct 11 '22 22:10

wero