本篇将介绍如何使用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
总结
本篇要介绍的内容到此结束了,源代码下载地址:https://files.cnblogs.com/files/yanweidie/NancyConsole.rar,更多关于Nancy的使用可以下载TaskManager源码进行研究http://code.taobao.org/svn/TaskManagerPub/Branch。下一篇介绍如何使用MEF实现通用的参数配置管理。