HTTP 内容协商是否遵循媒体类型参数

Does HTTP content negotiation respect media type parameters

提问人:Raedwald 提问时间:8/18/2015 最后编辑:Raedwald 更新时间:8/19/2015 访问量:2099

问:

HTTP 请求可以包含标头,指示客户端认为可接受的响应的媒体类型。服务器应通过提供与请求的媒体类型(其中之一)匹配的响应来响应请求。媒体类型可以包含参数。HTTP 是否要求此内容协商过程遵循参数AcceptContent-Type

也就是说,如果客户端请求

 Accept: application/vnd.example; version=2

(这里参数的值为 ),服务器可以提供媒体类型,但不能,服务器可以提供响应吗version2application/vnd.example; version=1application/vnd.example; version=2

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

服务器是否可以提供标记为

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

但是要使响应的正文实际上被编码为媒体类型?也就是说,对于响应的媒体类型的参数是对响应主体的不准确描述?application/vnd.example; version=1

Spring MVC 4.1.0 在进行内容协商时似乎不尊重媒体类型参数,并且给出的响应的媒体类型参数是对响应正文的不准确描述。这似乎是因为该方法不检查对象的参数。org.springframework.util.MimeType.isCompatibleWith(MimeType)MimeType

HTTP spring-MVC 内容协商

评论


答:

3赞 wero 8/18/2015 #1

HTTP/1.1 中内容协商的相关规范是 RFC2616 的第 14.1 节

它包含以下示例,与您的问题相关:

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

并给出优先级

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

所以我认为可以肯定地说,这是不同的媒体类型。 我也会考虑和不同。text/html;level=1text/htmltext/html;level=1text/html;level=2

因此,在您的示例中,我认为使用 406 错误而不是使用不同的媒体类型进行响应是正确的。

8赞 Raedwald 8/18/2015 #2

相关标准 RFC 7231 第 3.1.1.1 节对媒体类型进行了以下说明:

类型/子类型 CAN 后跟以下形式的参数 name=值对。

因此,标头可能包含媒体类型参数。它补充说:AcceptContent-Type

存在或不存在 参数可能对媒体类型的处理很重要, 取决于它在媒体类型注册表中的定义。

这表明使用参数类型的服务器代码应该注意它们,而不是简单地丢弃它们,因为对于某些媒体类型来说,它们很重要。它必须实现一些智能,以考虑媒体类型参数是否重要。

因此,Spring MVC 4.1.0 在进行内容协商时完全忽略参数似乎是错误的:该类使用不正确,或者该方法不正确。如果您为 Spring 提供了多个 HTTP 消息转换器,这些转换器仅在其支持的媒体类型的参数上有所不同,则 Spring 将无法可靠地选择媒体类型与请求的媒体类型完全匹配的 HTTP 消息转换器。org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessororg.springframework.util.MimeType.isCompatibleWith(MimeType)MimeType.isCompatibleWith(MimeType)


在第 3.1.1.5 节中,它描述了标题,它说:Content-Type

指示的媒体类型定义了这两个数据 格式以及接收方应如何处理该数据

由于媒体类型的参数通常可能会改变数据格式,因此Spring MVC 4.1.0的行为是错误的,因为提供的参数对响应的正文描述不准确:当两种类型同样具体时,返回而不是返回的方法是错误的。AbstractMessageConverterMethodProcessor.getMostSpecificMediaType(MediaType, MediaType)acceptTypeproduceTypeToUse


但是,讨论内容协商(主动协商的第 3.4.1 节指出:

用户代理不能依赖主动协商首选项 始终如一地兑现,因为源服务器可能无法实现 对请求的资源进行主动协商,或者可能决定 发送不符合用户代理的响应 首选项比发送 406(不可接受)响应要好。

因此,当服务器无法提供完全匹配时,允许服务器给出与请求的媒体类型参数不完全匹配的响应,作为后备。也就是说,它可能会选择使用响应正文和标头进行响应,尽管请求说 ,当且仅当无法生成有效响应时。application/vnd.example; version=1Content-Type: application/vnd.example; version=1Accept: application/vnd.example; version=2application/vnd.example; version=2


Spring 的这种明显不正确的行为已经有了 Spring 错误报告,SPR-10903。Spring 开发人员将其关闭为“按设计工作”,并指出

我不知道任何可以有效地将媒体类型与其参数进行比较的规则。这真的取决于媒体类型......如果您实际上尝试通过媒体类型实现 REST 版本控制,那么最常见的解决方案似乎是使用不同的媒体类型,因为它们的格式在版本之间明显发生了变化:

  • "application/vnd.spring.foo.v1+json"
  • "application/vnd.spring.foo.v2+json"

评论

0赞 jam01 8/7/2018
这是正当的。我已经在 Spring 上打开了一个问题,请去投票看看适当的更改是否获得批准。jira.spring.io/browse/SPR-17133