近來學習了一下Nancy這個框架,感覺挺好用的,就寫篇簡單的文章記錄一下大致用法,由於是剛接觸,寫的代碼
可能不規范,也沒有具體的分層。。莫吐槽。。。
Nancy的官網:http://nancyfx.org/
GitHub地址:https://github.com/NancyFx/Nancy
Nancy在文檔的介紹 -- 輕量級
" Nancy is a lightweight, low-ceremony, framework for building HTTP based services on .NET and Mono. "
Nancy有多種Hosting:
Hosting Nancy with ASP.NET
Hosting Nancy with WCF
Hosting Nancy with Azure
Hosting Nancy with OWIN
Web (Katana)
Self Hosting
Environment Variables
Conditional Pass-through
Hosting Nancy with Umbraco
Hosting Nancy with Nginx on Ubuntu
Hosting Nancy with FastCgi
Self Hosting Nancy
Implementing a Host
Accessing the client certificate when using SSL
本文寫的Demo是基於Hosting Nancy with ASP.NET。
開發環境 :win 10 + VS2015 Community + SqlServer 2012
廢話不多說,開始正題:
一、新建一個空的asp.net項目
建好之后空空如也
二、通過NuGet添加Nancy相關的Packages
主要是Nancy、Nancy.Hosting.Aspnet、Nancy.Viewengines.Razor這三個。其版本依次為1.4.3、1.4.1、1.4.3
我這里選擇的視圖引擎是Razor,習慣了,你們可以選擇其他,這個問題不大。
三、更新一下Razor
默認安裝Nancy.Viewengines.Razor之后,它會安裝 Microsoft.AspNet.Razor.2.0.30506.0,我們用的是最新版的3.2.3
在NuGet通過已安裝的Packages來更新
到這里,基本的工作已經OK了。
這里用到了Dapper,所以我也添加了它的引用。
注:這里是通過NuGet安裝的,所以它也在web.config里生成了一些配置。如果是手動添加引用的,注意要修改web.config。
四、加入css和javascript文件
允許我偷偷懶,這里的我是直接copy一般新建mvc項目的,就連頁面布局也是用的新建項目的模板。。
五、新建Modules、Models、ViewModels、Views四個文件夾
Modules-->相當於MVC中的Controllers文件夾
Models-->存放模型
ViewModels-->存放視圖模型
Views-->存放視圖
六、在Views下面建一個布局頁_Layout.cshtml
就新建的mvc模板copy過來,稍加修改

1 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic> 2 <!DOCTYPE html> 3 <html> 4 <head> 5 <meta charset="utf-8" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>@ViewBag.Title - Catcher's NancyDemo</title> 8 <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> 9 <link href="~/Content/Site.css" rel="stylesheet" /> 10 <link rel="shortcut icon" href="~/favicon.ico" /> 11 </head> 12 <body> 13 <div class="navbar navbar-inverse navbar-fixed-top"> 14 <div class="container"> 15 <div class="navbar-header"> 16 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 17 <span class="icon-bar"></span> 18 <span class="icon-bar"></span> 19 <span class="icon-bar"></span> 20 </button> 21 <a href="/" class="navbar-brand">NancyDemo</a> 22 </div> 23 <div class="navbar-collapse collapse"> 24 <ul class="nav navbar-nav"> 25 <li><a href="/">首頁</a></li> 26 <li><a href="/home/about">關於我們</a></li> 27 <li><a href="/home/contact">聯系我們</a></li> 28 <li><a href="/movie/">電影</a></li> 29 <li><a href="/movie-type/">類型</a></li> 30 </ul> 31 </div> 32 </div> 33 </div> 34 <div class="container body-content"> 35 @RenderBody() 36 <hr /> 37 <footer> 38 <p>© @DateTime.Now.Year - My ASP.NET Application</p> 39 </footer> 40 </div> 41 <script src="~/Scripts/jquery-1.10.2.min.js"></script> 42 <script src="~/Scripts/bootstrap.min.js"></script> 43 </body> 44 </html>
七、在Modules下新建一個HomeModules.cs,具體如下:

1 public class HomeModule : NancyModule 2 { 3 public HomeModule() 4 { 5 Get["/"] = _ => ShowHomePage(); 6 Get["/home/about"] = _ => ShowAboutPage(); 7 Get["/home/contact"] = _ => ShowContactPage(); 8 } 9 10 private dynamic ShowContactPage() 11 { 12 return View["Contact"]; 13 } 14 15 private dynamic ShowAboutPage() 16 { 17 return View["About"]; 18 } 19 20 private dynamic ShowHomePage() 21 { 22 return View["Index"]; 23 } 24 }
同時在Views文件夾下面新建一個Home的子文件夾,在把mvc模板中的Index.cshtml,About.cshtml,Contact.cshtml
copy過來稍加修改

1 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic> 2 @{ 3 ViewBag.Title = "首頁"; 4 Layout = "Views/_Layout.cshtml"; 5 } 6 <div class="jumbotron"> 7 <h1>NancyDemo</h1> 8 <p class="lead">Nancy is a lightweight, low-ceremony, framework for building HTTP based services on .NET and Mono. The goal of the framework is to stay out of the way as much as possible and provide a super-duper-happy-path to all interactions.</p> 9 <p><a href="http://nancyfx.org/" class="btn btn-primary btn-lg">Learn more »</a></p> 10 </div>

1 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic> 2 @{ 3 ViewBag.Title = "About"; 4 Layout = "Views/_Layout.cshtml"; 5 } 6 <h2>@ViewBag.Title.</h2> 7 <p>Use this area to provide additional information.</p>

1 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic> 2 @{ 3 ViewBag.Title = "Contact"; 4 Layout = "Views/_Layout.cshtml"; 5 } 6 <h2>@ViewBag.Title.</h2> 7 8 <address> 9 One Microsoft Way<br /> 10 Redmond, WA 98052-6399<br /> 11 <abbr title="Phone">P:</abbr> 12 425.555.0100 13 </address> 14 15 <address> 16 <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br /> 17 <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a> 18 </address>
然后F5,跑一下
已經能跑起來了。
下面就是結合數據庫了。
八、在MOdels下面建兩個類

1 public class Movie 2 { 3 public int MovieId { get; set; } 4 5 public string MovieName { get; set; } 6 7 public int MovieTypeId { get; set; } 8 9 public DateTime MovieAddTime { get; set; } 10 11 public virtual MovieType MovieType { get; set; } 12 13 public Movie() 14 { 15 MovieAddTime = DateTime.Now; 16 } 17 }

1 public class MovieType 2 { 3 public MovieType() 4 { 5 Movies = new HashSet<Movie>(); 6 } 7 8 9 public int TypeId { get; set; } 10 11 public string TypeName { get; set; } 12 13 public virtual ICollection<Movie> Movies { get; set; } 14 }
以Movie表為例,繼續下面的工作。
九、由於movie表中的數據展示,抽取出應該有的視圖模型
在ViewModels文件夾下面建立MovieViewModel.cs、MovieListViewModel.cs

1 public class MovieViewModel 2 { 3 public int MovieId { get; set; } 4 5 public string MovieName { get; set; } 6 7 public int MovieTypeId { get; set; } 8 9 public string MovieTypeName { get; set; } 10 11 public DateTime MovieAddTime { get; set; } 12 }

1 public class MovieListViewModel 2 { 3 public IEnumerable<MovieViewModel> Movies { get; set; } 4 }
十、現在就該寫寫煩人mvc控制器的東西了,在Modules文件夾下建立MovieModules.cs
把數據提取出來

1 public class MovieModule : NancyModule 2 { 3 public MovieModule() : base("/movie") 4 { 5 Get["/"] = _ => ShowMovieIndexPage(); 6 } 7 8 private readonly string _sqlconnection = 9 "Data Source=192.168.0.71;Initial Catalog=NancyDemo;User Id=sa;Password=dream_time1314;"; 10 11 public SqlConnection OpenConnection() 12 { 13 SqlConnection connection = new SqlConnection(_sqlconnection); 14 connection.Open(); 15 return connection; 16 } 17 18 private dynamic ShowMovieIndexPage() 19 { 20 using (IDbConnection conn = OpenConnection()) 21 { 22 string getAllMoviesStoredProcedure = @"up_GetAllMovies"; 23 MovieListViewModel viewModel = new MovieListViewModel 24 { 25 Movies = conn.Query<MovieViewModel>(getAllMoviesStoredProcedure, 26 null, null, true, null, CommandType.StoredProcedure) 27 }; 28 return View["Index", viewModel]; 29 } 30 } 31 }
然后就去編寫視圖
在Views下面新建一個Movie文件夾,用於存放相關視圖

1 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<MovieDemo.ViewModels.MovieListViewModel> 2 @{ 3 Layout = "Views/_Layout.cshtml"; 4 ViewBag.Title = "電影列表"; 5 } 6 <a href="/movie/create">添加</a> 7 <div> 8 <table class="table"> 9 <tr> 10 <th> 11 # 12 </th> 13 <th> 14 電影名稱 15 </th> 16 <th> 17 電影類型 18 </th> 19 <th> 20 添加時間 21 </th> 22 <th></th> 23 </tr> 24 @foreach (var item in Model.Movies) 25 { 26 <tr> 27 <td> 28 @item.MovieId 29 </td> 30 <td> 31 @item.MovieName 32 </td> 33 <td> 34 @item.MovieTypeName 35 </td> 36 <td> 37 @item.MovieAddTime 38 </td> 39 <td> 40 <a href="/movie/edit/@item.MovieId">修改</a> 41 <a class="delete" href="/movie/delete/@item.MovieId">刪除</a> 42 </td> 43 </tr> 44 } 45 </table> 46 </div>
F5跑一下
OK!
然后就是修改某條數據,
在MovieModule.cs 的構造函數中添加
1 Get["/edit/{movieId}"] = _ => ShowMovieEditPage(_.movieId); 2 Post["/edit/{movieId}"] = _ => 3 { 4 var movie = this.Bind<Movie>(); 5 return MovieEdit(movie); 6 };
然后添加ShowMovieEditPage和MovieEdit這兩個方法

1 private dynamic ShowMovieEditPage(int movieId) 2 { 3 string getOneMovieStoredProcedure = @"up_GetMovieByMovieId"; 4 string getALLTypeStoredProcedure = @"up_GetAllMovieTypes"; 5 DynamicParameters dynamicParameters = new DynamicParameters(); 6 dynamicParameters.Add("@MovieId", movieId); 7 8 using (IDbConnection conn = OpenConnection()) 9 { 10 var movieViewModel = conn.Query<MovieViewModel>(getOneMovieStoredProcedure, dynamicParameters, null, true, null, CommandType.StoredProcedure).SingleOrDefault(); 11 ViewBag.typeList = conn.Query<MovieType>(getALLTypeStoredProcedure, null, null, true, null, CommandType.StoredProcedure); 12 return View["Edit", movieViewModel]; 13 14 } 15 }

1 private dynamic MovieEdit(Movie movie) 2 { 3 string updateMovieStoredProcedure = @"up_UpdateMovieByMovieId"; 4 DynamicParameters dynamicParameters = new DynamicParameters(); 5 dynamicParameters.Add("@MovieId", movie.MovieId); 6 dynamicParameters.Add("@MovieName", movie.MovieName); 7 dynamicParameters.Add("@MovieTypeId", movie.MovieTypeId); 8 9 using (IDbConnection conn = OpenConnection()) 10 { 11 conn.Execute(updateMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure); 12 return Response.AsRedirect("/movie"); 13 } 14 }
添加一個Movie的Edit視圖

1 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<MovieDemo.ViewModels.MovieViewModel> 2 @{ 3 ViewBag.Title = "修改電影信息"; 4 Layout = "Views/_Layout.cshtml"; 5 } 6 7 <form action="/movie/edit/@Model.MovieId" method="post"> 8 <div class="form-group"> 9 <label class="control-label col-md-2">電影名稱</label> 10 <div class="col-md-10"> 11 <input type="text" class="form-control" id="MovieName" name="MovieName" value="@Model.MovieName" /> 12 </div> 13 </div> 14 <div class="form-group"> 15 <label class="control-label col-md-2">電影類型</label> 16 <div class="col-md-10"> 17 <select id="MovieTypeId" name="MovieTypeId" class="form-control"> 18 @foreach (var item in (System.Collections.Generic.List<MovieDemo.Models.MovieType>)ViewBag.typeList) 19 { 20 if (Model.MovieTypeId == item.TypeId) 21 { 22 <option selected="selected" value="@item.TypeId">@item.TypeName</option> 23 } 24 else 25 { 26 <option value="@item.TypeId">@item.TypeName</option> 27 } 28 } 29 30 </select> 31 </div> 32 </div> 33 <div class="form-group"> 34 <div class="col-md-offset-2 col-md-10"> 35 <input type="submit" value="修改" class="btn btn-default" /> 36 </div> 37 </div> 38 </form>
然后即可跑起來了。
其中用到了一個模型綁定,需要添加Nancy.ModelBinding引用,有了這個綁定,省了很多事!!
var movie = this.Bind<Movie>();
同理,增加和刪除也是同樣的做法,下面是MovieModule.cs的完整代碼

1 using MovieDemo.Models; 2 using MovieDemo.ViewModels; 3 using Dapper; 4 using Nancy; 5 using Nancy.ModelBinding; 6 using System.Data; 7 using System.Data.SqlClient; 8 using System.Linq; 9 10 namespace MovieDemo.Modules 11 { 12 public class MovieModule : NancyModule 13 { 14 public MovieModule() : base("/movie") 15 { 16 Get["/"] = _ => ShowMovieIndexPage(); 17 18 Get["/edit/{movieId}"] = _ => ShowMovieEditPage(_.movieId); 19 Post["/edit/{movieId}"] = _ => 20 { 21 var movie = this.Bind<Movie>(); 22 return MovieEdit(movie); 23 }; 24 25 Get["/create"] = _ => ShowMovieCreatePage(); 26 Post["/create"] = _ => 27 { 28 var movie = this.Bind<Movie>(); 29 return MovieCreate(movie); 30 }; 31 32 Get["/delete/{movieId}"] = _ => MovieDelete(_.movieId); 33 } 34 35 private readonly string _sqlconnection = 36 "Data Source=192.168.0.71;Initial Catalog=NancyDemo;User Id=sa;Password=dream_time1314;"; 37 38 public SqlConnection OpenConnection() 39 { 40 SqlConnection connection = new SqlConnection(_sqlconnection); 41 connection.Open(); 42 return connection; 43 } 44 45 private dynamic MovieDelete(int movieId) 46 { 47 string deleteMovieStoredProcedure = @"up_DeleteMovieByMovieId"; 48 DynamicParameters dynamicParameters = new DynamicParameters(); 49 dynamicParameters.Add("@MovieId", movieId); 50 using (IDbConnection conn = OpenConnection()) 51 { 52 conn.Execute(deleteMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure); 53 return Response.AsRedirect("/movie"); 54 } 55 } 56 57 private dynamic MovieCreate(Movie movie) 58 { 59 string createMovieStoredProcedure = @"up_InsertMovie"; 60 DynamicParameters dynamicParameters = new DynamicParameters(); 61 dynamicParameters.Add("@MovieName", movie.MovieName); 62 dynamicParameters.Add("@MovieTypeId", movie.MovieTypeId); 63 dynamicParameters.Add("@MovieAddTime", movie.MovieAddTime); 64 65 using (IDbConnection conn = OpenConnection()) 66 { 67 conn.Execute(createMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure); 68 return Response.AsRedirect("/movie"); 69 } 70 } 71 72 private dynamic ShowMovieCreatePage() 73 { 74 string getALLTypeStoredProcedure = @"up_GetAllMovieTypes"; 75 using (IDbConnection conn = OpenConnection()) 76 { 77 MovieTypeListViewModel viewModel = new MovieTypeListViewModel 78 { 79 MovieTypes = conn.Query<MovieType>(getALLTypeStoredProcedure, null, null, true, null, CommandType.StoredProcedure) 80 }; 81 return View["Create", viewModel]; 82 } 83 } 84 85 private dynamic MovieEdit(Movie movie) 86 { 87 string updateMovieStoredProcedure = @"up_UpdateMovieByMovieId"; 88 DynamicParameters dynamicParameters = new DynamicParameters(); 89 dynamicParameters.Add("@MovieId", movie.MovieId); 90 dynamicParameters.Add("@MovieName", movie.MovieName); 91 dynamicParameters.Add("@MovieTypeId", movie.MovieTypeId); 92 93 using (IDbConnection conn = OpenConnection()) 94 { 95 conn.Execute(updateMovieStoredProcedure, dynamicParameters, null, null, CommandType.StoredProcedure); 96 return Response.AsRedirect("/movie"); 97 } 98 } 99 100 private dynamic ShowMovieEditPage(int movieId) 101 { 102 string getOneMovieStoredProcedure = @"up_GetMovieByMovieId"; 103 string getALLTypeStoredProcedure = @"up_GetAllMovieTypes"; 104 DynamicParameters dynamicParameters = new DynamicParameters(); 105 dynamicParameters.Add("@MovieId", movieId); 106 107 using (IDbConnection conn = OpenConnection()) 108 { 109 var movieViewModel = conn.Query<MovieViewModel>(getOneMovieStoredProcedure, dynamicParameters, null, true, null, CommandType.StoredProcedure).SingleOrDefault(); 110 ViewBag.typeList = conn.Query<MovieType>(getALLTypeStoredProcedure, null, null, true, null, CommandType.StoredProcedure); 111 return View["Edit", movieViewModel]; 112 113 } 114 } 115 116 private dynamic ShowMovieIndexPage() 117 { 118 using (IDbConnection conn = OpenConnection()) 119 { 120 string getAllMoviesStoredProcedure = @"up_GetAllMovies"; 121 MovieListViewModel viewModel = new MovieListViewModel 122 { 123 Movies = conn.Query<MovieViewModel>(getAllMoviesStoredProcedure, 124 null, null, true, null, CommandType.StoredProcedure) 125 }; 126 return View["Index", viewModel]; 127 } 128 } 129 } 130 }
在添加相應的視圖即可完成。
十一、下面還需要簡單講一下Bootstrapper
這個東西可以簡單認為是引導程序,你可以自定義一些鬼東西,把它當作一個容器來用,
還可以通過它來實現依賴注入,里面有個默認的 TinyIoC 。。。。。詳細介紹請看https://github.com/NancyFx/Nancy/wiki/Bootstrapper
下面是我的CustomBootstrapper.cs配置

1 public class CustomBootstrapper : DefaultNancyBootstrapper 2 { 3 protected override void ConfigureApplicationContainer(TinyIoCContainer container) 4 { 5 base.ConfigureApplicationContainer(container); 6 //container.Register<IMoviesRepository, MoviesRepository>(); 7 //container.Register<IMovieTypesRepository, MovieTypesRepository>(); 8 } 9 protected override void ConfigureConventions(NancyConventions nancyConventions) 10 { 11 base.ConfigureConventions(nancyConventions); 12 nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Scripts")); 13 nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Content")); 14 } 15 }
依賴注入我是沒有用的,所以我注釋了。用過Ninject、Autofac、Unity這些的,相信依賴注入這一塊理解起來So easy!
Nancy也有相應的拓展
https://github.com/NancyFx/Nancy.Bootstrappers.Autofac
https://github.com/NancyFx/Nancy.Bootstrappers.Unity
https://github.com/NancyFx/Nancy.Bootstrappers.Ninject
還有一塊是ConfigureConventions
這一塊似乎是用來處理靜態文件(css、js)的,跟Mvc中的BundleConfig很類似
到這里,開發工作已經完成。下面是部署的時候了。
十二、將做的MovieDemo部署到Linux下
Linux環境及配置
CentOS 6.7 + mono 4.2.2 + Jexus 5.8.0
將發布過后的目錄上傳到centos的 /var/www/ 中,然后在 /usr/jexus/siteconf 中建立一個新的配置文件
重新啟動jexus即可。
下面上幾張運行的圖
完全的無縫兼容。
本篇的完整代碼已托管到GitHub
下載地址:https://github.com/hwqdt/Demos/tree/master/NancyDemoWithHostingAspnet
下雨天,心情壓抑,寫篇bolg來拯救自己的好心情 ~_~
后面有時間會寫基於owin的demo