最近在使用 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错误”。