標籤:Rails

Rails API 送了 `Accept: application/json, */*` 拿到 406

之前在 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

  1. https://github.com/rails/rails/issues/9940 ↩︎