禾川資訊 Grass Brook

Text

HTTP ACCEPT of mobile devices in Rails based sites

最近在 hoptoad 上看到一些詭異的 log,似乎是網站升到 Rails 3 後才出現的:

ActionView::MissingTemplate: Missing template posts/show with {:formats=>["application/youtube-client", "*/*"], :handlers=>[:rjs, :rxml, :rhtml, :builder, :erb], :locale=>[:"zh-TW", :"zh-TW"]} in view paths

沒錯,怎麼會有 format 是 “application/youtube-client”, “/“,這到底是什麼鬼玩意送出來的?(狀態顯示為才疏學淺)詳細看了一下 HTTP header 資訊發現

HTTP_ACCEPT "*/*, application/youtube-client"
HTTP_USER_AGENT "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; HD2_T8585; Windows Phone 6.5)"
HTTP_UA_OS "Windows CE (Pocket PC) - Version 5.2"

原來是 Windows Phone 這樣的 mobile 裝置送的。

但由於數量不少,於是再繼續看看是不是有其他奇怪的 log 有類似情況的:

  • 居然有 PSP 的,好樣的:
ActionView::MissingTemplate: Missing template posts/show with {:handlers=>[:rjs, :builder, :rhtml, :rxml, :erb], :locale=>[:"zh-TW", :"zh-TW"], :formats=>["*/*;q=0.01"]} in view paths
HTTP_ACCEPT "*/*;q=0.01"
HTTP_USER_AGENT "Mozilla/4.0 (PSP (PlayStation Portable); 2.00)"
  • 華為科技的產品,是分享器嗎?
ActionView::MissingTemplate: Missing template photos/show with {:formats=>["application/vnd.wap.wmlc", "application/vnd.wap.wmlscriptc", "text/vnd.wap.wml", "image/vnd.wap.wbmp", "*/*"], :locale=>[:"zh-TW", :"zh-TW"], :handlers=>[:rjs, :rxml, :rhtml, :builder, :erb]} in view paths
HTTP_ACCEPT "application/vnd.wap.wmlc, application/vnd.wap.wmlscriptc, text/vnd.wap.wml, image/vnd.wap.wbmp, */*"
HTTP_USER_AGENT "Mozilla/5.0 (Linux; U; Android 2.1-update1; zh-tw; MB525 Build/JRDNEM_U3_2.51.0) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17"
HTTP_VIA "(infoX WAP Gateway), HTTP/1.1, Huawei Technologies"
HTTP_X_UP_BEAR_TYPE "WCDMA"
  • 微軟的行動辦公室?
ActionView::MissingTemplate: Missing template pages/welcome with {:formats=>["text/*"], :handlers=>[:rjs, :rxml, :rhtml, :builder, :erb], :locale=>[:"zh-TW", :"zh-TW"]} in view paths
HTTP_ACCEPT "text/*"
HTTP_USER_AGENT "Microsoft Office Mobile /14.0"
  • 傳說中的魔王 IE6 / Windows XP:
ActionView::MissingTemplate: Missing template posts/show with {:formats=>["image/gif", "image/x-xbitmap", "image/jpeg", "image/pjpeg"], :locale=>[:"zh-TW", :"zh-TW"], :handlers=>[:rjs, :rxml, :rhtml, :builder, :erb]} in view paths
HTTP_ACCEPT "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg"
HTTP_USER_AGENT "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
  • 完全沒有 HTTP_USER_AGENT 的資訊,似乎是 POST 動作後導向失敗造成的?
ActionView::MissingTemplate: Missing template digiphoto_posts/show with {:formats=>[:html, :text, :js, :css, :ics, :csv, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json], :locale=>[:"zh-TW", :"zh-TW"], :handlers=>[:rjs, :rxml, :rhtml, :builder, :erb]} in view paths
HTTP_ACCEPT "*/*"
  • 同上,有正確的 format,但是呈現不同的 HTTP_ACCEPT 樣式(多筆 log 資料彙整):
ActionView::MissingTemplate: Missing template digiphoto_posts/show with {:formats=>[:html], :locale=>[:"zh-TW", :"zh-TW"], :handlers=>[:rjs, :rxml, :rhtml, :builder, :erb]} in view paths
HTTP_ACCEPT "application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/msword, application/vnd.ms-excel, application/x-shockwave-flash, */*"
HTTP_ACCEPT "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
HTTP_ACCEPT "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"
HTTP_ACCEPT "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, application/vnd.ms-excel, */*"

看到這樣的現象,直覺反應是,事情絕對不是想像中的那麼簡單,必定有惡魔藏在細節裡。

仔細地整理一下,上述的錯誤,所要求的頁面 format 應該都要判斷為 :html 才是正確的,而其中共通的特性是 HTTP_ACCEPT 都不外乎是下列幾種:

"*/*", "*/*;q=0.01", "text/*", "application/youtube-client", "application/vnd.wap.wmlc"

並且似乎也跟 mobile 裝置有關,這就讓我想到有人抱怨 HTC 手機不能順利瀏覽 T 客邦網站這件事。於是乎抓了幾天的 log 來分析,居然有驚奇的發現

https://gist.github.com/9a396c56a73b57d5ad72

行動裝置的 HTTP_ACCEPT 長得都不太一樣,同樣是 Android 系統,卻沒幾個能正確送出 text/html,幾乎都是送 application/xml,太令人訝異了。

那 Rails 3 到底是怎麼利用 HTTP_ACCEPT 資訊,又怎麼判斷 format 的呢?

直接查原始碼可以發現 ActionDispatch::Http::MimeNegotiation 有個 formats 方法,當找不到 paramater[:format] 這個傳遞資訊時,會由 HTTP_ACCEPT 猜測一個適當的 format,最後才預設為 [Mime::HTML]。(Rails 3.0.7)

而這就是問題所在,猜測的邏輯沒有包含到這些狀況,上述那些怪異的 HTTP_ACCEPT,在 Rails 3.0.7 以前(含)並不能正確的處理,於是乎輕則看到這些錯誤 log,重則直接吐 502 給你。那可不妙,我可是要支援 mobile 裝置啊~

幸好,活躍的 Rails 圈有人提出了問題並獲得解答:

  • https://rails.lighthouseapp.com/projects/8994/tickets/6022
  • https://rails.lighthouseapp.com/projects/8994/tickets/5833

但解法目前只有 apply 在 rails master branch,我可是要解燃眉之急呀,於是自己搞了個 patch 來修補 Rails 3.0.7:

https://gist.github.com/33c10952fe3cadaec3c4

直接抓回來放在 config/initializers 後,重新啟動 application 就行了。

這個 patch 同時解決了兩件事:

  • 針對特殊的 HTTP_ACCEPT,如 “/”, “/;q=0.01”, “text/*” 都能正確判斷 format 是 [Mime::JS] 或 [Mime::HTML]。
  • 針對行動裝置送的 application/xxx 這類型 HTTP_ACCEPT 也能返回正確的 Mime::SET。

如此一來就能好好面對行動裝置的挑戰了。

Posted on Wednesday, May 11 2011.
禾川資訊 Grass Brook We are a studio focused on Ruby, Rails and Agile Development.
Ask me anything Submit
Previous