HTTP内容协商是否遵守媒体类型参数
问题内容:
HTTP请求可以包括Accept
标头,指示客户端将发现可接受的响应的媒体类型。服务器应通过提供一个Content- Type
与所请求的媒体类型(之一)相匹配的响应来响应该请求。媒体类型可以包括 参数 。HTTP是否要求此内容协商过程尊重 参数 ?
也就是说,如果客户要求
Accept: application/vnd.example; version=2
(此处version
参数的值为2
),服务器可以提供media-type application/vnd.example; version=1
,但不能提供media-type ,服务器是否可以application/vnd.example; version=2
通过以下方式提供响应:
Content-Type: application/vnd.example; version=1
服务器可以提供标有标签的响应吗
Content-Type: application/vnd.example; version=2
但是对于响应的主体是否实际上被编码为media-type application/vnd.example; version=1
?就是说,对于响应的媒体类型参数是否是对响应主体的不正确描述?
看起来Spring MVC
4.1.0在进行内容协商时不考虑媒体类型参数,而是给出响应,而响应的媒体类型参数是对响应主体的不准确描述。这似乎是因为该org.springframework.util.MimeType.isCompatibleWith(MimeType)
方法未检查MimeType
对象的参数。
问题答案:
相关标准RFC
7231第3.1.1.1节
对媒体类型说了以下几点:
类型/子类型后面可以是名称=值对形式的参数。
所以,Accept
和Content-Type
头可能包含媒体类型参数。它增加了:
参数的存在或不存在对于媒体类型的处理可能很重要,这取决于媒体类型注册表中的参数定义。
这表明使用参数类型的服务器代码应注意它们,而不是 简单地 丢弃它们,因为对于某些媒体类型,它们 将
很重要。在考虑媒体类型参数是否重要时,它必须实现一些技巧。
因此,Spring MVC
4.1.0在进行内容协商时完全忽略参数似乎是错误的:该类org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
使用不正确org.springframework.util.MimeType.isCompatibleWith(MimeType)
,或者该MimeType.isCompatibleWith(MimeType)
方法不正确。如果为Spring提供了几个HTTP消息转换器,这些HTTP消息转换器仅在其支持的媒体类型的参数上有所不同,Spring将不会可靠地选择其媒体类型与请求的媒体类型完全匹配的HTTP消息转换器。
在3.1.1.5节中,它描述Content- Type
标题的地方是:
指示的媒体类型定义了数据格式以及收件人打算如何处理该数据
由于通常媒体类型的参数可能会改变数据格式,因此Spring MVC
4.1.0的行为是错误的,因为它提供的响应主体描述不准确:AbstractMessageConverterMethodProcessor.getMostSpecificMediaType(MediaType, MediaType)
返回acceptType
而不是方法错误。这produceTypeToUse
两种类型的含义是否相同。
但是,在讨论内容协商( 主动协商
)的3.4.1节中指出: __
用户代理不能依赖始终遵循主动协商的首选项,因为源服务器可能未对请求的资源实施主动协商,或者可能决定发送不符合用户代理首选项的响应比发送406(不接受)响应。
因此,服务器 被 允许给不响应 正好 符合要求的媒体类型的参数,如回退时,它不能提供精确匹配。也就是说,尽管请求说, 当且仅当
不可能生成有效响应时,它才可以选择使用application/vnd.example; version=1
响应主体和Content-Type: application/vnd.example; version=1
标头进行响应。Accept: application/vnd.example; version=2
__application/vnd.example; version=2
Spring的这种明显不正确的行为已经具有Spring
bug报告SPR-10903。Spring开发人员将其作为“按设计工作”关闭,并指出
我不知道有效地比较媒体类型及其参数的任何规则。这实际上取决于媒体类型…如果您实际上试图通过媒体类型实现REST版本控制,则似乎最常见的解决方案是使用不同的媒体类型,因为它们的格式在版本之间显然发生了变化:
- “
application/vnd.spring.foo.v1+json
”- “
application/vnd.spring.foo.v2+json
”