Nancy中關於靜態文件(Js、css等)的處理
今天我們來談談Nancy中的靜態文件(JavaScript,CSS等)該如何處理。
在前面的Demo中,我們也已經用到了這一塊的內容,
但並沒有深入理解,只是停留在使用的層面上。
在進入今天的正題之前,我們先來簡單看看我們熟悉的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're 11 // 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 五個頁面
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>index</title> 5 <meta charset="utf-8" /> 6 </head> 7 <body> 8 <a href="/default">page with default convention</a><br /> 9 <a href="/custom">page with custom convention</a><br /> 10 <a href="/other">page without custom convention</a><br /> 11 <a href="/sub">page sub</a> 12 </body> 13 </html>index.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>default</title> 5 <meta charset="utf-8" /> 6 <link href="../../content/style.css" rel="stylesheet" /> 7 </head> 8 <body> 9 <p>這是引用 /content/sytle.css 的頁面(預設的convention配置)</p> 10 </body> 11 </html>default.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>custom</title> 5 <meta charset="utf-8" /> 6 <link href="../../assets/style.css" rel="stylesheet" /> 7 </head> 8 <body> 9 <p>這是引用 /assets/style.css 的頁面(自定義Convention配置)</p> 10 </body> 11 </html>custom.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>other</title> 5 <meta charset="utf-8" /> 6 <link href="../../other/style.css" rel="stylesheet" /> 7 </head> 8 <body> 9 <p>這是引用 /other/style.css 的頁面(沒有Convention配置)</p> 10 </body> 11 </html>other.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>sub</title> 5 <meta charset="utf-8" /> 6 <link href="../../assets/sub/style.css" rel="stylesheet" /> 7 </head> 8 <body> 9 <p>這是引用 /assets/sub/style.css 的頁面(自定義Convention配置,子文件夾測試)</p> 10 </body> 11 </html>sub.html
六、在"引導程式"中配置Convention(至關重要的一步)
新建DemoBootstrapper.cs,使其繼承DefaultNancyBootstrapper並且override我們的ConfigureConventions
1 public class DemoBootstrapper : DefaultNancyBootstrapper 2 { 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配置的理解也是類似的,所以這裡就不再累贅了。
從"引導程式"的ConfigureConventions中可以知道,無論我們自定義多少個Convetion,
都是要添加到StaticContentsConventions這個集合中的。
九、簡單總結
ConfigureConventions 與 BundleConfig 都是用於處理靜態文件的,有相同之處,也有各自的特點。
在項目開發過程中,我們可能會根據習慣把css、javascript這些靜態文件放在自己喜歡的位置,
但是在Nancy中這個的處理需要十分註意的是,只要我們沒有將css和javascript文件放在content中時,就一定要記得在Convention中進行配置!
否則頁面死活不是我們期待的那樣。。。。
所以我個人感覺這塊內容不是很友好,一旦不小心忘了配置,而且發現頁面樣式不對,首先想到的是不是樣式的路徑寫錯了
而不會直接考慮到Nancy的Convention配置這一層面。
為此,提醒各位使用Nancy的朋友,並建議各位:只要您的項目用到了靜態文件,請務必要override我們的ConfigureConventions !!