我們先來簡單看看我們熟悉的ASP.NET MVC中是如何管理我們項目中的這些靜态檔案呢?
其實當我們建立一個MVC的項目時,已經生成了一個“模闆”讓我們參考,
這個“模闆”就是App_Start下面的 BundleConfig.cs

1 public class BundleConfig 2 { 3 // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 4 public static void RegisterBundles(BundleCollection bundles) 5 { 6 bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 7 "~/Scripts/jquery-{version}.js")); 8 bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 9 "~/Scripts/jquery.validate*"));10 // Use the development version of Modernizr to develop with and learn from. Then, when you're11 // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.12 bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(13 "~/Scripts/modernizr-*"));14 bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(15 "~/Scripts/bootstrap.js",16 "~/Scripts/respond.js"));17 bundles.Add(new StyleBundle("~/Content/css").Include(18 "~/Content/bootstrap.css",19 "~/Content/site.css"));20 }21 }

其中的ScriptBundle和StyleBundle分别是用于管理js和css的類,這兩個類都是繼承了Bundle這個類!
它位于System.Web.Optimization程式集,如果想要用這個功能,記得添加引用喔!
那我們要怎麼使用這個呢?
現在假設在根目錄下面有css和js兩個檔案夾,裡面分别存放着Style1.css、Style2.css和js1.js、js2.js
下面就來看看怎麼把它交于Bundle管理

1 bundles.Add(new ScriptBundle("~/bundles/js").Include(2 "~/js/js1.js",3 "~/js/js2.js"));4 bundles.Add(new StyleBundle("~/bundles/css").Include(5 "~/css/Style1.css",6 "~/css/Style2.css"));

其中的“~/bundles/js”和"~/bundles/css"是虛拟路徑!
然後就是在頁面中使用(就是用我們剛才的虛拟路徑)
1 @Styles.Render("~/bundles/css")2 @Scripts.Render("~/bundles/js")
是不是很友善呢!更多關于Bundle的内容可以參考
http://www.asp.net/mvc/overview/performance/bundling-and-minification
因為它不是我們今天的主要内容,隻是拿來與Nancy中的靜态檔案處理形成對比,便于我們的了解。
下面就來看看Nancy中的靜态檔案怎麼處理。
為了示範的友善,這裡僅使用css。
先看看具體的使用,然後再簡單分析其内部的實作。
一、建立一個空的asp.net應用程式
在這個應用程式中添加我們需要的引用,這裡可以根據前面介紹的,
按自己喜歡的方式、方法來添加Nancy相關的引用
二、建立Modules
老規矩:Modules檔案夾、HomeModule.cs

1 public class HomeModule : NancyModule 2 { 3 public HomeModule() 4 { 5 Get["/"] = _ => 6 { 7 return View["index"]; 8 }; 9 10 Get["/default"] = _ =>11 {12 return View["default"];13 };14 15 Get["/custom"] = _ =>16 {17 return View["custom"];18 };19 20 Get["/other"] = _ =>21 {22 return View["other"];23 };24 25 Get["/sub"] = _ =>26 {27 return View["sub"];28 };29 }30 }

三、建立content、assets、other三個檔案夾,以及在assets檔案夾下面建立一個sub檔案夾用于存放樣式表
四、分别添加一些簡單的樣式在這些檔案夾中
content下面的sytle.css内容如下
1 body {background-color:#00ffff;}2 p {font-size:xx-large; }
assets和other下面的style.css内容如下
1 body {background-color:#00ffff;}2 p {font-size:xx-large;color:#ff0000;}
assets/sub下面 的style.css内容如下
1 body {background-color:#808080;}2 p {font-size:xx-large;color:#ff0000;}
五、添加Views
老規矩:Views檔案夾、Home檔案夾
添加 index.html、default.html、custom.html、other.html、sub.html 五個頁面
index.html
default.html
custom.html
other.html
sub.html
六、在"引導程式"中配置Convention(至關重要的一步)
建立DemoBootstrapper.cs,使其繼承DefaultNancyBootstrapper并且override我們的ConfigureConventions

1 public class DemoBootstrapper : DefaultNancyBootstrapper2 {3 protected override void ConfigureConventions(NancyConventions nancyConventions)4 {5 base.ConfigureConventions(nancyConventions);6 nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("assets"));7 }8 }

七、運作結果
八、結果分析與探讨
1、default.html 用的樣式是在content下面的,能正常加載樣式!
2、custom.html用的樣式是在assets下面的,能正常加載樣式!
3、other.html用的樣式是在other下面的,不能正常加載樣式!!
4、sub.html用的樣式是在assets/sub下面的,能正常加載樣式!
很明顯,結果有點出乎我們的意料,我們在Convetion的配置中,隻配置了一項!
就是對assets檔案夾進行了處理。其他都沒有手動配置!
但是在content下面的樣式是能夠正常顯示的!!而other下面的是不能正常顯示的!!assets的子檔案夾sub的樣式也正常顯示!!
這個給人貌似不是很合理的感覺。
看看Network的内容會發現other下面的樣式表不是不能正常加載那麼簡單,而是直接給個404!!!
那我們就深入的去看看這裡面到底發生了什麼事吧!
fork一份Nancy的源碼,clone到本地,來看看個是以然。(其實上面的例子我就是在源碼上面添加的一個Demo)
首先看看我們今天的主題Conventions下面的東西
其中從名字就可以看出跟我們今天的主題靜态檔案,相關的就有7個!!
但這并不是我們的出發點,我們的出發點是下面這個!
1 protected override void ConfigureConventions(NancyConventions nancyConventions)2 {3 base.ConfigureConventions(nancyConventions);4 nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("assets"));5 }
Convention的配置指引着我們要先去看看NancyConvetions這個類
在其構造方法中調用了 BuildDefaultConventions 這個方法

1 /// <summary>2 /// Initializes a new instance of the <see cref="NancyConventions"/> class.3 /// </summary>4 public NancyConventions()5 {6 this.BuildDefaultConventions();7 }

這就很明顯的告訴我們,無論如何,它都會有預設的Conventions!!而且看了裡面的實作
會發現,預設的Convention還不僅僅是一個!!而是包含多個。這裡我們僅探讨關于靜态檔案的。

1 private void BuildDefaultConventions() 2 { 3 var defaultConventions = 4 AppDomainAssemblyTypeScanner.TypesOf<IConvention>(ScanMode.OnlyNancy); 5 this.conventions = defaultConventions 6 .Union(AppDomainAssemblyTypeScanner.TypesOf<IConvention>(ScanMode.ExcludeNancy)) 7 .Select(t => (IConvention)Activator.CreateInstance(t)); 8 foreach (var convention in this.conventions) 9 {10 convention.Initialise(this);11 }12 }

現在我們就該去找關于靜态檔案的預設Convetion
發現剛才的7個相關中,有一個DefaultStaticContentsConventions
它實作了IConvention接口(Nancy中基本都是接口化程式設計,很Nice!!)。
其中的初始化方法中

1 public void Initialise(NancyConventions conventions)2 {3 conventions.StaticContentsConventions = new List<Func<NancyContext, string, Response>>4 {5 StaticContentConventionBuilder.AddDirectory("Content")6 };7 }

是不是跟我們自定義配置幾乎相差無幾!!我想看到AddDirectory的參數"Content",大家也應該都知道了
為什麼我們的content下面的樣式,沒有配置都能正常加載(我去,它預設都是content,能不正常加載麼。。)
裡面的StaticContentConventionBuilder又是何方神聖呢?
這個是靜态基于目錄的幫助類
裡面有兩個主要的方法 AddDirectory和AddFile ,都是傳回Func<NancyContext, string, Response>類型的東東。
看名字都已經知道大概實作了什麼東西,一個基于某個目錄,一個基于某個單獨的檔案。
這裡需要注意一下這兩個方法的參數!
還有一些其他的東西是用于拼接目錄和處理Cache的。
把這幾個重要的類看了一下,是不是對這個靜态檔案的預設配置也清晰了不少呢?
然後對自定義Convetion配置的了解也是類似的,是以這裡就不再累贅了。