JAX-RS/Jersey 中的 HTTP 内容协商冲突?

发布于 2024-10-21 03:08:03 字数 1690 浏览 9 评论 0原文

我很喜欢 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 之间得到不同的结果。当我导航到端点 URL 时,Firefox 将映射到 HTML 端点,而 Chrome 将触发 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 是否有一些在管道中添加过滤器以在路由请求之前对其进行调整的概念?

I am enjoying the auto HTTP content negotiation of JAX-RS (specifically Jersey), i.e. its ability to route my resources by "Accept" and/or "Content-Type" headers. But I'm finding that sometimes it doesn't give me enough control when there is a conflict.

For example, consider the following endpoints:

@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);
    }
}

I will get different results between Firefox and Chrome. Firefox will map to the HTML endpoint while Chrome will trigger the XML endpoint when I navigate each to the endpoint URL. The difference between them is the order of the listed MIME types in their Accept headers. Chrome sends the following:

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

Versus in Firefox it lists HTML first:

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

Seems logical that it would match the first entry when all are weighted the same. But in my case I am getting the different results than I want so it would be nice to determine a better method for tie-breaking.

My question: short of injecting header information into these methods and performing the media type processing myself, is there a way to "tweak the weights" so to speak in the event of a tie? For instance, can I tell it to always trump XML with HTML? My RESTful clients are very explicit about what type they want back but browsers are notoriously sloppy with Accept headers. (Personally I think they should weight HTML slightly above XML as this is what users expect but it's a little late for that.)

Alternatively, can I perform my own custom content negotiation only once in some centralized location? I'm not opposed to writing this logic out manually but not if it means applying it to every single instance of my resources. Does JAX-RS have some concept of adding a filter to the pipeline to tweak requests before they are routed?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

客…行舟 2024-10-28 03:08:03

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

(我从此来源了解到这一点,并且我在这里为自己写了一条小便条)

There is a mechanism in Jersey to override the relative degree of preference from the HTTP Accept header. Just add a parameter "qs" to the @Produces annotation that you want to take precedence. In your case:
@Produces("text/html;qs=2")
Note that the http "q" values range from 0-1 and the Jersey "qs" values should be >= 1 (1 is default).

(I learned about this from this source, and I wrote a little note for myself here)

面犯桃花 2024-10-28 03:08:03

正如泽西岛用户指南所述:

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

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

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

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

As the Jersey User Guide states:

If both are equally acceptable then the former will be chosen because it occurs first.

From what I know, this leaves you with two possibilities/hacks:

  1. Append the file extension to your
    URIs to override the Accept header

  2. Write a servlet filter that
    overwrites the Accept header for
    those User Agents

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文