之前在 Rails API mode 遇到 Client 在 Header 送了 Accept: application/json, text/html, */*
結果拿到 406 的問題,原先以為是 Client 送法不對(Server 只吃 JSON)。
查了一下 RFC2616 發現這樣的送法應該沒有什麼問題才對:
If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.
Server 應該在能夠回傳任一指定 Accept type 時回傳指定 Type,如果都不能夠回傳時才 406,尤其 Client 還用了 */*
,應該是要無論如何都不會 406 才對……
詳細查了一下發現[1],Rails 會將 Client 送出多個 Mime type 搭配上 */*
(例如:Accept: x-any-type/x-any-subtype, */*
或 Accept: */*, x-any-type/x-any-subtype
)的行為認為是 Browser 預設行為(據說是老 IE)而強制在 ActionDispatch
複寫成 HTML type。
但是 API mode 的 Rails 本來就不可能返回 HTML,結果就導致 Server 噴 406……
解法也很簡單,就是把判斷用部分蓋掉就好,這裡選擇把 Regexp 複寫掉:
ActionDispatch::Http::MimeNegotiation.send :remove_const, :BROWSER_LIKE_ACCEPTS
ActionDispatch::Http::MimeNegotiation.const_set :BROWSER_LIKE_ACCEPTS, /NEVER_MATCHED_MIM