【问题标题】:HTTP content negotiation conflicts in JAX-RS/Jersey?JAX-RS/Jersey 中的 HTTP 内容协商冲突?
【发布时间】:2011-07-12 04:15:15
【问题描述】:

我喜欢 JAX-RS(特别是 Jersey)的自动 HTTP 内容协商,即它能够通过“Accept”和/或“Content-Type”标头路由我的资源。但是我发现有时在发生冲突时它并没有给我足够的控制权。

例如,考虑以下端点:

@Path("/order")
public class OrderController {

    @GET
    @Path("{orderID: \\d+}")
    @Produces("text/html")
    public View getOrderView(@PathParam("orderID") long id) {
        Order order = this.getOrderData(id);
        return new OrderView(order);
    }

    @GET
    @Path("{orderID: \\d+}")
    @Produces({"application/json", "application/xml"})
    public Order getOrderData(@PathParam("orderID") long id) {
        return new OrderService.findOrder(id);
    }
}

我会在 Firefox 和 Chrome 之间得到不同的结果。 Firefox 将映射到 HTML 端点,而 Chrome 将在我导航到每个端点 URL 时触发 XML 端点。它们之间的区别在于其 Accept 标头中列出的 MIME 类型的顺序。 Chrome 发送以下内容:

User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

与 Firefox 相比,它首先列出 HTML:

User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

当所有的权重相同时,它会匹配第一个条目,这似乎是合乎逻辑的。但在我的情况下,我得到的结果与我想要的不同,所以最好确定一个更好的打破平局的方法。

我的问题:没有将标头信息注入这些方法并自己执行媒体类型处理,有没有办法在出现平局时“调整权重”?例如,我可以告诉它总是用 HTML 胜过 XML 吗?我的 RESTful 客户端对他们想要返回的类型非常明确,但浏览器对 Accept 标头的草率是出了名的。 (我个人认为他们应该将 HTML 的权重略高于 XML,因为这是用户所期望的,但为时已晚。)

或者,我可以在某个集中位置只执行一次我自己的自定义内容协商吗?我不反对手动编写此逻辑,但如果这意味着将其应用于我的资源的每个实例,则不反对。 JAX-RS 是否有一些概念,即在管道中添加过滤器以在请求被路由之前对其进行调整?

【问题讨论】:

    标签: rest http-headers jersey jax-rs content-negotiation


    【解决方案1】:

    Jersey 中有一种机制可以覆盖 HTTP Accept 标头的相对偏好程度。只需将参数“qs”添加到您想要优先的@Produces 注释。在你的情况下: @Produces("text/html;qs=2") 请注意,http "q" 值的范围是 0-1,而 Jersey "qs" 值应 >= 1(默认为 1)。

    (我是从this source了解到的,我给自己写了一个小笔记here)

    【讨论】:

    • 我发现这个解决方案存在一些问题。首先,它将 ";qs=2" 一直传递到 Content-Type 标头中的客户端。但交易破坏者是它的重量太重了。例如,带有“Accept: application/json, text/javascript, /; q=0.01”但带有“text/html;qs=2”和“application/json”的jQuery请求,Jersey发送基于 JSON 的 HTML。
    【解决方案2】:

    正如泽西岛User Guide 所说:

    如果两者都同样可接受,则将选择前者,因为它首先出现。

    据我所知,这给你留下了两种可能性/黑客:

    1. 将文件扩展名附加到您的 用于覆盖 Accept 标头的 URI

    2. 编写一个 servlet 过滤器 覆盖 Accept 标头 那些用户代理

    【讨论】:

    • 谢谢,Servlet 过滤器似乎工作得很好。有趣的是,我找到了一个结合了你的两个建议的人;他正在编写一个 servlet 过滤器,以便能够用 Accept 标头替换文件扩展名。尽管他的标头检查区分大小写,但他的代码中有一个错字。不过很有趣:zienit.nl/blog/2010/01/rest/…
    • 这是一个修改后的 Servlet 过滤器,它添加了对文件扩展名的支持并修复了 WebKit Accept 标头排序。 gist.github.com/865216
    • @mckamey 您写的 cmets 中的这些添加应该变成答案,因为 cmets 并不意味着永远存在
    猜你喜欢
    • 2018-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多