2011-03-09 60 views
5

我很享受JAX-RS(特別是Jersey)的自動HTTP內容協商,即它通過「接受」和/或「內容類型」標題來路由我的資源的能力。但是我發現有時在衝突時它不能給我足夠的控制權。JAX-RS/Jersey中的HTTP內容協商衝突?

例如,請考慮以下端點:

@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客戶非常清楚他們想要回到什麼類型,但瀏覽器在接受頭文件方面出了名。 (我個人認爲他們應該重量HTML略高於XML,因爲這是用戶所期望的,但它是一個有點晚了。)

或者,我可以在一些集中的位置上執行一次自己的自定義內容協商?我不反對手動編寫這個邏輯,但如果這意味着將它應用到我的資源的每一個實例,則不是這樣。 JAX-RS是否有一些在管道中添加過濾器來調整請求的概念?

回答

1

作爲澤西User Guide狀態:

如果兩者都同樣可接受的,因爲它第一次出現那麼前者將被選擇。

據我所知,這讓你有兩種可能性/黑客:

  1. 附加文件擴展你的 URI來覆蓋Accept頭

  2. 寫一個servlet過濾器 將覆蓋Accept標頭 那些用戶代理

+1

謝謝,這個Servlet過濾器似乎工作的偉大。有趣的是,我找到了一個結合你兩個建議的人;他正在編寫一個servlet過濾器,以便能夠用Accept頭替換文件擴展名。他的代碼中有一個錯字,因爲他的頭文件檢查區分大小寫。有趣的是:http://www.zienit.nl/blog/2010/01/rest/control-jax-rs-content-negotiation-with-filters – mckamey 2011-03-10 20:48:27

+1

下面是一個修改後的Servlet過濾器,它增加了對文件擴展名的支持並修復了WebKit接受標題排序。 https://gist.github.com/865216 – mckamey 2011-03-11 00:01:07

+0

@mckamey在你寫的評論中的這些增加應該變成一個答案,因爲評論並不意味着永遠生存 – hiergiltdiestfu 2017-01-27 14:47:33

9

Jersey中有一種機制可以覆蓋來自HTTP Accept頭的相對優先級。只需將一個參數「qs」添加到您想要優先考慮的@Produces註釋中即可。在你的情況下: @Produces("text/html;qs=2") 請注意,http「q」值的範圍從0到1,Jersey的「qs」值應該大於等於1(默認值爲1)。

(我得知這個情況從this source,我寫了張條子給自己here

+3

我發現這個解決方案有一些問題。首先,它將「; qs = 2」一直傳遞給Content-Type頭中的客戶端。但這個交易斷路器是因爲重量太重了。例如,Jersey使用「Accept:application/json,text/javascript,*/*; q = 0.01」但帶有「text/html; qs = 2」和「application/json」的jQuery請求,Jersey通過JSON發送HTML。 – mckamey 2011-04-15 20:48:25