本篇將介紹如何使用Nancy.Host實現脫離iis的Web應用,在開源任務管理平台TaskManagerV2.0代碼里面已經使用了Nancy.Host實現自宿主的Web應用。學習Nancy之前最好了解一下ASP.NET MVC,因為Nancy和MVC實在是太相似了。
閱讀目錄
Nancy介紹
Nancy是一個輕量級的用來創建基於HTTP的服務的框架,該框架的可以運行在.net或者mono上。 Nancy處理和mvc類似的DELETE, GET, HEAD, OPTIONS, POST, PUT,PATCH請求,如果你有mvc開發的經驗相信可以快速入門。最重要的一點可以讓你的Web應用脫離IIS的束縛。
public class Module : NancyModule { public Module() { Get["/greet/{name}"] = x => { return string.Concat("Hello ", x.name); }; } }
特征
- 自底向上全套都是新構建的,移除了對其他框架的引用和限制。
- Run anywhere. Nancy 能夠在ASP.NET/IIS,OWIN,Self-hosting中運行。
- 集成支持各種View engine(Razor, Spark, dotLiquid, SuperSimpleViewEngine...)
資源
Github:https://github.com/NancyFx/Nancy 官網:http://nancyfx.org 使用介紹:http://liulixiang1988.github.io/nancy-webkuang-jia.html
創建第一個應用
1.創建控制台程序,引用相關Package
使用Nuget安裝Nancy,Nancy.Hosting.Self,Nancy.Viewengines.Razor,Newtonsoft.Json四個Package


2.監聽端口
class Program { static void Main(string[] args) { try { int port = 9000; string url = string.Format("http://localhost:{0}", port); var _host = new NancyHost(new Uri(url)); _host.Start(); Process.Start(url); Console.WriteLine("站點啟動成功,請打開{0}進行瀏覽",url); } catch (Exception ex) { Console.WriteLine("站點啟動失敗:"+ex.Message); } Console.ReadKey(); } }
public class HomeModule : NancyModule { public HomeModule() { //主頁 Get["/"] = r => { return Response.AsRedirect("/Home/Index"); }; //主頁 Get["/Home/Index"] = r => { return View["index", "測試站點"]; }; ///桌面 Get["/DestTop"] = r => { return View["DestTop"]; }; } }
小知識點:Nancy里面的所有控制器都需要繼承NancyModule類,類比MVC的控制器都需要繼承Controller類
創建視圖
新建index.cshtml視圖內容如下:
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase @{ ViewBag.Title = @Model; } @section style{ } 我是第一個Nancy應用 @section scripts{ <script> </script> }
至此一個簡單的應用完成了,運行項目后你會發現提示找不到視圖index,是因為index視圖沒有拷貝到 bin\Debug目錄下,添加視圖的時候需要手工設置文件屬性->始終復制到輸出目錄。如果嫌這樣設置太麻煩可以采取我后面提供的一種方案。


使用技巧
僅上面這點東西做一個Web應用是完全不夠的,下面講解一下進階內容和使用小技巧。
1.使用CSS和JS等靜態資源
要想在視圖里面使用靜態資源需要設置允許訪問的靜態資源類型,通過繼承DefaultNancyBootstrapper類重寫ConfigureConventions方法
public class CustomBootstrapper : DefaultNancyBootstrapper { protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); //pipelines.BeforeRequest += ctx => //{ // return null; //}; pipelines.AfterRequest += ctx => { // 如果返回狀態嗎碼為 Unauthorized 跳轉到登陸界面 if (ctx.Response.StatusCode == HttpStatusCode.Unauthorized) { ctx.Response = new RedirectResponse("/login?returnUrl=" + Uri.EscapeDataString(ctx.Request.Path)); } else if (ctx.Response.StatusCode == HttpStatusCode.NotFound) { ctx.Response = new RedirectResponse("/Error/NotFound?returnUrl=" + Uri.EscapeDataString(ctx.Request.Path)); } }; pipelines.OnError += Error; } protected override IRootPathProvider RootPathProvider { get { return new CustomRootPathProvider(); } } /// <summary> /// 配置靜態文件訪問權限 /// </summary> /// <param name="conventions"></param> protected override void ConfigureConventions(NancyConventions conventions) { base.ConfigureConventions(conventions); ///靜態文件夾訪問 設置 css,js,image conventions.StaticContentsConventions.AddDirectory("Content"); } protected override void ConfigureApplicationContainer(TinyIoCContainer container) { base.ConfigureApplicationContainer(container); //替換默認序列化方式 container.Register<ISerializer, CustomJsonNetSerializer>(); } private dynamic Error(NancyContext context, Exception ex) { //可以使用log4net記錄異常 ex 這里直接返回異常信息 return ex.Message; } }
這里設置的根目錄下的Content文件夾下所有文件都可以被訪問,我們可以將所有靜態資源放在該文件夾下
2.使用視圖模版
視圖模版使用方式和mvc的一模一樣,在視圖文件夾下創建_ViewStart.cshtml視圖,內容如下
@{ Layout = "/Shared/_Layout.cshtml"; }
_Layout.cshtml里面放置頁面公共的內容比如公共css和js,定義相關占位符
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> <title>@ViewBag.Title</title> <link rel="shortcut icon" type="image/x-icon" href="~/Content/Image/favicon.ico"> <link href="~/Content/Css/style.css" rel="stylesheet"> @RenderSection("style", required: false) </head> <body> @RenderBody() <script src="~/Content/Scripts/jquery-1.10.2.min.js"></script> @RenderSection("scripts", required: false) </body> </html>
3.控制器返回JSON值控制
默認Nancy使用的是自己內置的JSON序列化庫,個人傾向於使用JSON.NET庫。所以通過設置替換成了JSON.NET。在CustomBootstrapper的ConfigureApplicationContainer容器里面替換了序列化庫
/// <summary> /// 使用Newtonsoft.Json 替換Nancy默認的序列化方式 /// </summary> public class CustomJsonNetSerializer : JsonSerializer, ISerializer { public CustomJsonNetSerializer() { ContractResolver = new DefaultContractResolver(); DateFormatHandling = DateFormatHandling.IsoDateFormat; Formatting = Formatting.None; NullValueHandling = NullValueHandling.Ignore; } public bool CanSerialize(string contentType) { return contentType.Equals("application/json", StringComparison.OrdinalIgnoreCase); } public void Serialize<TModel>(string contentType, TModel model, Stream outputStream) { using (var streamWriter = new StreamWriter(outputStream)) using (var jsonWriter = new JsonTextWriter(streamWriter)) { Serialize(jsonWriter, model); } } public IEnumerable<string> Extensions { get { yield return "json"; } } }
4.返回文件
Get["/Home/Download"] = r => { string path = AppDomain.CurrentDomain.BaseDirectory+@"\Content\UpFile\使用說明.docx"; if (!File.Exists(path)) { return Response.AsJson("文件不存在,可能已經被刪除!"); } var msbyte = default(byte[]); using (var memstream = new MemoryStream()) { using (StreamReader sr = new StreamReader(path)) { sr.BaseStream.CopyTo(memstream); } msbyte = memstream.ToArray(); } return new Response() { Contents = stream => { stream.Write(msbyte, 0, msbyte.Length); }, ContentType = "application/msword", StatusCode = HttpStatusCode.OK, Headers = new Dictionary<string, string> { { "Content-Disposition", string.Format("attachment;filename={0}", HttpUtility.UrlPathEncode(Path.GetFileName(path))) }, {"Content-Length", msbyte.Length.ToString()} } }; };
rd/s/q $(TargetDir)Content rd/s/q $(TargetDir)Views xcopy $(ProjectDir)\Content\*.* $(TargetDir)Content\ /s/d/r/y xcopy $(ProjectDir)\Views\*.* $(TargetDir)Views\ /s/d/r/y
總結
本篇要介紹的內容到此結束了,源代碼下載地址:http://files.cnblogs.com/files/yanweidie/NancyConsole.rar,更多關於Nancy的使用可以下載TaskManager源碼進行研究http://code.taobao.org/svn/TaskManagerPub/Branch。下一篇介紹如何使用MEF實現通用的參數配置管理。
