2015年3月8日 星期日

ASP.NET MVC - 讀取 Area 路徑下的靜態檔案

前幾天同事遇到了一個問題,在 ASP.NET MVC 網站裡要如何能夠正常的讀取放在 Area 根目錄下的靜態檔案?

如果靜態檔案是放在 ASP.NET MVC 的根目錄下的話,這還好解決,但問題是當這個靜態檔案是放在 Area 裡面的時候就會變得有些棘手,因為原本可行的解決方式在 Area 裡是行不通的,於是在就開始四處尋找能夠解決的方法,在搞了一個下午(中間還離開一個小時去開會以及進來好幾個需求插件),最後總算找出了一個解法,這邊就做個記錄,提供給大家作為參考,也尋求是否有更好的做法。

 


事發主因是一個 swf 檔案,用來播放影片,當影片播放的時候就會另外去讀取控制影片播放的另一個 swf 檔案,在以往的 ASP.NET WebForms 網站裡,因為目錄結構就等同於網站的路徑結構,所以當這些檔案都放在某一個階層的目錄下是可以正常讀取檔案,但是在 ASP.NET MVC 網站裡就會遇到 Route 的問題,因為目錄結構不等同於網站的路徑結構,而製作 swf 的人並不會知道網站是用什麼技術來開發的,所以就會直覺的將路徑給寫死,而導致播放影片的時候就無法抓到控制影片的 swf 檔案的正確路徑。

有關讀取 ASP.NET MVC 網站下的靜態檔案在 Bruce Chen 與 Will 保哥的文章裡都有提供說明與示範:

KingKong Bruce記事: ASP.NET MVC 5如何正確加入robots.txt

The Will Will Web | ASP.NET MVC 4 在 .NET 4.0 與 .NET 4.5 的專案範本差異

 

開發環境:Window 8.1, Visual Studio 2013 Update 4, IISExpress

在一般的狀況下,如果你有一個靜態檔案的路徑是必須要放在網站根目錄下的話,你可以直接放在 ASP.NET MVC 網站的檔案根目錄,不需要特殊的 Route 設定,也不需要修改 Web.Config,也不需要其他第三方套件的支援,就是直接放在根目錄下,例如下圖所示(以下的範例將以圖片檔來做示範,而不是使用 swf 檔案)

image

那麼使用瀏覽器讀取這個靜態圖片檔的就直接使用「http:// localhost:25116/VS2013.jpg」這樣的路徑,

image

 

但如果在 ASP.NET MVC 網站專案裡增加 Areas,然後希望靜態檔案的路徑是要在某個 Area 的路徑下,例如:「http:// localhost:25116/Sample1/VS2013.jpg」,在直覺上應該就會將靜態檔案給放到 Area 的根目錄下,照理說應該就可以在瀏覽器使用我們所想的路徑去讀取到檔案,

image

但是…… 事情並不是那麼的單純,會直接給你看 404 的。

image

 

題外話,如果你有靜態檔案不想放在網站根目錄下的 Content 目錄或根目錄的其他檔案,而是想要將靜態檔案給放在 Area 下面的話,那麼可以在 Area 下建立目錄,但是讀取檔案就不能直接相對網址路徑去做讀取,例如我在 Sample1 這個 Area 裡建立 Content,然後把靜態檔案給放進去,

image

而讀取這個 Area 的 Content 目錄裡的靜態檔案就必須連同 Areas 與 Area 名稱給帶到路徑中,如下:

image

網頁執行結果

image

 

再回到主題,我們已經知道無法直接讀取放在 Area 根目錄下的靜態檔案,也知道要把靜態檔案給放在 Area 裡另外建立的目錄,如此一來才能讀取到檔案,但是路徑必須要再加上 Areas 以及 Area 名稱,但假如讀取靜態檔案的路徑一定要是在 Area 根目錄下的話,這就必須要動點手腳才能夠辦到了。

 

Step.1 - Web.Config

必須要在 Web.Config(網站根目錄下的那一個)的 system.webServer 內的 handlers 增加「HtmlFileHandler」,並且指定路徑下的哪些副檔名的檔案才要經過 HtmlFileHandler 的處理。

image

 

Step.2 - Sample1AreaRegistration.cs

接著到 Area「Sample1」裡的 Sample1AreaRegistration.cs 去增加新的 Route 設定,這邊的設定是要讓凡是以讀取 Area 根目錄路徑下的 Jpg 檔案就會走這個 Route,然後由 DefaultController 的 Jpg Action 方法去處理。

image

 

Step.3 - DefaultController

這邊要在 DefaultController 裡增加 Jpg Action 方法,這一個 Action 方法就是做檔案讀取的處理,然後最後以 FileResult 將靜態檔案給回傳,這麼一來就可以維持讀取路徑,也可以能夠回傳檔案,

image

不過這樣有影響的就是效能,因為讀取 Area 根目錄下的 JPG 靜態檔案就必須得進入 Controller 的 Action 方法,但也只有讀取 Area 根目錄下的 JPG 檔案才會走這樣的處理方式,所以一開始在 Web.Config 裡所增加的 HtmlFileHandler 路徑設定就需要比較嚴謹,必須限定路徑,不然就會影響到整個網站的 JPG 檔案讀取。

執行結果

image

image

image

而原本在 Sample1/Content 目錄下的 VS2013.jpg 並不會受到影響,檔案的讀取路徑還是與原本的一樣。

image

 

也許有些人會質疑,為什麼不讓 DefaultController 的 Jpg Action 方法直接去讀取 JPG 靜態檔案呢?

其實也是可以啦,我只是要讓這個範例可以比較凸顯如何讀取放在 Area 根目錄下的檔案,檔案怎麼放、要放在什麼地方、如何管理,就看開發者的管理方式或團隊開發規範,只要讓 Action 方法讀得到檔案就好。

image

修改後的檔案讀取是不會受到影響的,

image

image

 


ASP.NET MVC 或 ASP.NET Web API 的 Route 設定總是永遠讓人捉摸不定,像是這個案例最原始的檔案是 swf,最根本的解決方法還是請製作 swf 的設計師以及網站開發人員可以先溝通好路徑的設定,不然往後這樣的情況還是會層出不窮。

Route 設定的難搞問題總是伴隨著客戶的需求而來,這星期還不只遇到文章裡所提到的這一件,有機會再說說其他的 Route 設定案例。

 

參考連結

MSDN - httpHandlers 項目 (ASP.NET 設定結構描述)

Jon Galloway - ASP.NET MVC Routing - Intercepting file requests like Index.html, and what it teaches about how Routing works

KingKong Bruce記事: ASP.NET MVC 5如何正確加入robots.txt

The Will Will Web | ASP.NET MVC 4 在 .NET 4.0 與 .NET 4.5 的專案範本差異

 

以上

2 則留言:

  1. 謝謝你分享此範例,讓我在實作上得到很大的幫助,謝謝~

    回覆刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。