当客户端发送Accept:* / *时,将text / html优先于其他内容类型

时间:2013-06-06 19:31:25

标签: spring-mvc content-negotiation

我正在尝试使用Spring MVC 3.2编写一个使用JSON和HTML的控制器。我有两种处理方法可以产生不同的内容类型:

@Controller
public class FooController {
    @RequestMapping(value="/foo", produces="text/html")
    public String fooHTML() {
        // ...
    }

    @RequestMapping(value="/foo", produces="application/json")
    public String fooJSON() {
        // ...
    }
}

如果客户端的Accept标头包含text/htmlapplication/json

,则效果非常好

...但是那时有Internet Explorer。如上所述here,IE的Accept标头各不相同,但它从不包含text/html,并且最后始终有*/*。当Spring收到来自IE的请求时,它看不到任何内容类型直接等于我的控制器生成的内容类型,但是,锁定到*/*通配符,它​​(正确地)决定两个映射都适用。

面对多个匹配的处理程序映射,Spring(在RequestMappingHandlerMapping bean中)对映射进行排序,基本上等于字典顺序,选择第一个映射,然后继续。从我的角度来看,问题在于此流程会优先application/json优先于text/html除非客户专门请求text/html ,否则我宁愿返回application/json - 这样,我就可以向像IE这样的愚蠢客户端提供HTML,为内容类型精明的客户端提供JSON喜欢我API的用户。

有没有人知道这样做的方法,不需要扩展RequestMappingHandlerMapping来对处理程序进行不同的排序?你有任何简单的解决方法吗?

注意:我已尝试按照on the Spring blog所述在ContentNegotiationManager中设置默认内容类型。它没有解决我的问题,因为该设置仅在未指定Accept标头时生效。

2 个答案:

答案 0 :(得分:1)

一种解决方案是将fooJSON()优先级降低value参数。

实际上,模式/foo/foo{1}是等效的。但是,第二个被认为是“更通用的”并且最后使用了:

@Controller
public class FooController {
    @RequestMapping(value="/foo", produces="text/html")
    public String fooHTML() {
        // ...
    }

    @RequestMapping(value="/foo{1}", produces="application/json")
    //                         ^^^------- changed here
    public String fooJSON() {
        // ...
    }
}

这样:

  • text/html转到fooHTML()
  • application/json转到fooJSON()
  • */*转到fooHTML()
  • 其他任何内容都会产生406 - Not Acceptable错误

答案 1 :(得分:1)

我发现解决此问题的一种方法是在您的请求映射中添加一个ALL mimetype,以提供响应的HTML版本。

@RequestMapping(value="/foo", produces={"text/html", "*/*"})
public String fooHTML() {
    // ...
}

这意味着HTML响应适用于任何以前未映射的mime类型。在我的情况下,需要这种行为,但如果你需要返回406(不可接受)的响应代码,那么这对你来说不会有用。