近来学习了一下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项目的,就连页面布局也是用的新建项目的模板。。
五、新建Moudles、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下新建一个HomeModule.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:[email protected]">[email protected]</a><br /> 17 <strong>Marketing:</strong> <a href="mailto:[email protected]">[email protected]</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文件夹下建立MovieModule.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