最近在使用 MVC 開發的時候,遇到一個對我來說“奇怪的問題”,就是使用 BundleTable 進行 CSS、JS 文件綁定,然后使用 Styles.Render、Scripts.Render 進行獲取,但總是獲取不到綁定的 CSS、JS 文件,然后報“404錯誤”,話說再多,不如一個代碼示例。
BundleConfig 配置代碼:
public class BundleConfig
{
// For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/test.js").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"));
bundles.Add(new StyleBundle("~/bundles/test.css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
}
}
視圖獲取:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
@Styles.Render("~/bundles/test.css")
@Scripts.Render("~/bundles/test.js")
</head>
運行結果:
這個問題我相信大家應該都遇到過,當然前提條件是按照我上面那種寫法,問題出來了,那該如何解決呢?因為你不知道問題出在哪,所以只能進行反復的嘗試,最后我無意間把 .css 和 .js 后綴去掉就可以了,在上一個開發項目我就是這么干的,然后現在開發的項目又遇到這個問題,這就引起了我的重視,當然這不是一個解決方法,只是你不知道它背后的東西罷了。
小標簽:當使用 VS 調試模式時,即 web.config 中 debug="true",使用 BundleConfig.RegisterBundles 進行注冊是沒有效果的,但是可以展示,只不過沒有起到“綁定”文件的作用,解決方式是,需要手動在 Application_Start 代碼中添加:BundleTable.EnableOptimizations = true;
除了把 .css 和 .js 后綴去掉,網上搜索,還有一種解決方法就是,在 web.config 添加如下配置:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
</modules>
</system.webServer>
runAllManagedModulesForAllRequests 這個東西,之前我也遇到過,但不是像這次使用 Styles.Render、Scripts.Render 引起的,而是使用 MapRoute 進行 .htm 文件路由配置的時候,出現“404”錯誤,然后在 web.config 添加上面的配置就可以了,我還紀錄了一篇博文:【記錄】ASP.NET MVC MapRoute .htm 不起作用,哎,當時并沒有深入進行研究 runAllManagedModulesForAllRequests,只是認為既然能解決問題就行了,現在一想,其實心里有點后怕,為什么這樣說?看下面就知道了。
runAllManagedModulesForAllRequests 到底是什么東西呢?其實從它名字上面,你就可以明白一點,這也就是命名好的好處啊,咳咳,說白了,其意思就是為所有 Modules 管理請求的一個開關,如果設置為 true,就是把所有請求到 Modules 的通道打通了,沒有任何阻攔,那 Modules 又是什么呢?字面意思可以理解為“模塊”或“單元”的意思,它是屬于 Web 服務器的東西,和 Web 應用程序不太相關,Web 應用程序只是對它發起一個請求,Modules 的相關東西,可以看下這一篇非常好的文章:IIS Modules Overview。
使用 IIS 部署站點的時候,在點擊站點,右側會有一個“主頁”,我們會看到 Modules 的“身影”:
點擊“模塊”,就可以看到 IIS 所有的默認 Modules:
所有 Module 的具體說明可以查看:Module Reference,我們看一個等會我們要用到的 Module-StaticFileModule(靜態文件管理模塊):
- Description: Sends out static files with the file extension .html, .jpg, as well as many others. The staticContent/mimeMap configuration collection determines the list of file extensions.
- Configuration sections: system.webServer/staticContent
- Dependencies: None.
- Potential issues when removing this module: Static files no longer become served. Requests for files return a 404 Not Found error indicating that no handler was matched.
上面說到,使用 MapRoute 進行 .htm 文件路由配置,出現“404錯誤”,我原本是想通過配置 StaticFileModule 進行解決,試過之后發現不行,staticContent/mimeMap 配置中,IIS 默認是有 .htm 配置的,具體為:<mimeMap fileExtension=".htm" mimeType="text/html"/>
,詳細博文介紹:IIS 6中mimemap屬性的默認設置,而且最重要的是 StaticFileModule 是管理所有靜態文件請求的,其實 MapRoute 進行 .htm 文件路由配置的請求,是到不了 StaticFileModule 模塊處理的,要不然早就可以訪問了,URL 路由配置有一個單獨的模塊-UrlMappingsModule,這部分內容后面再了解。
至于 Styles.Render、Scripts.Render 獲取包含后綴綁定的“404錯誤”,可以用下面配置進行解決:
<system.webServer>
<modules>
<add name="BundleModule" type="System.Web.Optimization.BundleModule" />
</modules>
</system.webServer>
其實上面這兩個問題的“另類”解決方式,歸根到底就是想避免使用 runAllManagedModulesForAllRequests="true"
,為什么要避免使用它?當然是有原因的,在說這個之前,再來看一個有意思的問題,可能和這個不太相關,但我還是想說一下。
先來看一段 web.config 配置:
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="MembershipCookie"
loginUrl="Login.aspx"
protection="All"
timeout="525600"
slidingExpiration="true"
enableCrossAppRedirects="true"
path="/" />
</authentication>
<authorization>
<deny users="?" />
</authorization>
</system.web>
<location path="Default.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>
這段代碼表示什么意思呢?authorization 配置的用戶訪問類型為“普通用戶授權后才能訪問”,location/authorization 是對某一頁面進行身份驗證,針對 Default.aspx 的身份驗證類型為“匿名可訪問”,并且 Default.aspx 為此站點的默認訪問文檔,可以理解為此站點出了 Default.aspx,其他頁面訪問都是需要身份驗證的,然后跳轉到 Login.aspx。
運行后,你會發現其實并不是這么回事,比如訪問 www.mysite.com,按照配置應該會訪問 www.mysite.com/Default.aspx,但是你會發現,它會跳轉到 www.mysite.com/Login.aspx,為什么會這樣呢?其實是 IIS 版本更新的問題,Stack Overflow 中詳細的問題描述:ASP.NET 2.0 and 4.0 seem to treat the root url differently in Forms Authentication,在他問題描述中,嘗試用 UrlRewriter,但是還是沒有起到效果,最后做了一個測試:
ASP.NET Version Url Behaviour
-------------------------------------------------------------------------
2.0 http://example.com Renders Default.aspx
2.0 http://example.com/Default.aspx Renders Default.aspx
4.0 http://example.com Redirects to Login.aspx
4.0 http://example.com/Default.aspx Renders Default.aspx
相關的兩篇 IIS 更新說明:
- A update is available that enables certain IIS 7.0 or IIS 7.5 handlers to handle requests whose URLs do not end with a period
- Authorization fails for requests to the Default Document after KB980368 is installed in Internet Information Services (IIS) 7.0 or 7.5
第一篇博文主要是說明 IIS 的一個更新,具體內容可以簡化為:After this patch is applied, ASP.NET 4 applications can handle requests for extensionless URLs. Therefore, managed HttpModules that run prior to handler execution will run. 什么意思?就是處理程序增加了一個"*"的映射,也就是說,比如文中提到的,更新之前只能訪問如下 URL:www.example.com/ExampleSite/ExampleFile.,注意后面是有“點”的,也就是只能訪問有擴展名的 URL,更新之后,可以訪問無擴展名的 URL,也就是可以把那個“點”去掉,這樣蘊含什么意思呢?注意上面英文的后面一句話,這句話就說明了后面那篇博文的問題原因。
后面一篇博文主要也是說明,上面 Stack Overflow 中所描述的那個問題,比如我訪問 www.mysite.com,更新之前,會根據配置文件中的“默認文檔”,找到相關頁面后,再根據 web.config 中的身份驗證配置,進行處理并顯示,如果按照上面的 web.config 配置,訪問的是 www.mysite.com/Default.aspx(匿名用戶),但是更新后,IIS 就可以處理無擴展名的 URL,得到響應的 URL 后,會立馬交給 Modules 進行處理,首先就是 UrlAuthentication、FromsAuthentication 模塊進行身份驗證處理,發現是“非法用戶”,然后就跳轉到了 www.mysite.com/Login.aspx,而直接訪問 www.mysite.com/Default.aspx,會先走配置文件的 location 身份驗證說明,因為更新針對的是“無擴展名”的 URL,而 www.mysite.com/Default.aspx 是有擴展名的 URL,說了那么多,微軟最后給出的解決方案是:
<configuration>
<system.webServer>
<handlers>
<remove name="ExtensionlessUrl-Integrated-4.0" />
<remove name="ExtensionlessUrl-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrl-ISAPI-4.0_32bit" />
</handlers>
</system.webServer>
</configuration>
其實這個問題和 runAllManagedModulesForAllRequests 不太相關,但它可以帶你思考 URL 和 Modules 之間的“微妙關系”,至于為什么避免使用 runAllManagedModulesForAllRequests="true"
,我貼出兩篇博文:
- The art of simplicity: Optimize the performance of your web applications: Don’t use runAllManagedModulesForAllRequests="true”.
- Don't use runAllManagedModulesForAllRequests="true" when getting your MVC routing to work
這篇博文就到這,后面解決一下不配置 runAllManagedModulesForAllRequests="true"
,使用 MapRoute 進行 .htm 文件路由配置,出現的“404錯誤”。
參考資料:
- IIS Modules Overview : The Official Microsoft IIS Site
- ASP.NET Bundling and Minification Returning File Not Found (404) - Scott On Writing.NET
- The art of simplicity: Optimize the performance of your web applications: Don’t use runAllManagedModulesForAllRequests="true”.
- What is the correct usage of "runAllManagedModulesForAllRequests" in ASP.NET MVC2/3? - Stack Overflow
- asp.net mvc - Why is my CSS bundling not working with a bin deployed MVC4 app? - Stack Overflow
- 更新程序使某些 IIS 7.0 或 IIS 7.5 處理程序來處理請求的 Url 不以句點結尾
- Authorization fails for requests to the Default Document after KB980368 is installed in Internet Information Services (IIS) 7.0 or 7.5
- ASP.NET 2.0 and 4.0 seem to treat the root url differently in Forms Authentication - Stack Overflow
- Don't use runAllManagedModulesForAllRequests="true" when getting your MVC routing to work
- web.config中authorization下的location中的path的設置 - J-Pei - 博客園
- IIS 6中mimemap屬性的默認設置 - 逸樂太子 - 博客園
文章列表