最近在 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 有類似情況的:
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"
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)"
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 "*/*"
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 圈有人提出了問題並獲得解答:
但解法目前只有 apply 在 rails master branch,我可是要解燃眉之急呀,於是自己搞了個 patch 來修補 Rails 3.0.7:
https://gist.github.com/33c10952fe3cadaec3c4
直接抓回來放在 config/initializers 後,重新啟動 application 就行了。
這個 patch 同時解決了兩件事:
如此一來就能好好面對行動裝置的挑戰了。