一、简单描述
Before:如果返回null,拦截器将主动权转给路由;如果返回Response对象,则路由不起作用。
After : 没有返回值,可以在这里修改或替换当前的Response。
OnError : 返回值与Before相似,引发的错误或异常时的控制代码可以写在这里。
这三兄弟的大致作用,看名字,也可以这样简单的理解:
Before:处理之前要干的事。(返回null,继续处理;返回Response对象,不再做要干的那件事,换做Response对象要干的事)
After : 处理之后要干的事。
OnError : 处理出错了要干的事。
这三兄弟在NancyModule中的定义如下
1 public AfterPipeline After { get; set; } 2 public BeforePipeline Before { get; set; }3 public ErrorPipeline OnError { get; set; }
而这三个Pipeline分别继承了
AsyncNamedPipelineBase<TAsyncDelegate, TSyncDelegate>和NamedPipelineBase<TDelegate>
所以与他们有关的就主要包含在5个类中!具体的放在最后来看一下!
二、简单用法
我们可以在Module中直接使用Before/After/OnError这三个
也可以在Bootstrapper中重写RequestStartup或者ApplicationStartup来实现
当然也可以自定义,只要实现IRequestStartup或者IApplicationStartup接口也可以完成相应的工作
下面我们就分别来说明一下
用法一:直接在Module中使用
定义一个BaseModule,具体如下:
1 public class BaseModule : NancyModule 2 { 3 public BaseModule() 4 { 5 //写法一 6 Before += ctx => { 7 System.Diagnostics.Debug.WriteLine("BaseModule---Before"); 8 return null; 9 };10 After += ctx => {11 System.Diagnostics.Debug.WriteLine("BaseModule---After");12 };13 OnError += (ctx, ex) => {14 System.Diagnostics.Debug.WriteLine("BaseModule---OnError");15 System.Diagnostics.Debug.WriteLine(ex.ToString());16 return null;17 };18 //写法二19 //Before += MyBefore;20 //After += MyAfter;21 //OnError += MyOnError;22 }23 private Response MyBefore(NancyContext ctx)24 {25 System.Diagnostics.Debug.WriteLine("BaseModule---Before----写法二");26 return null;27 }28 private void MyAfter(NancyContext ctx)29 {30 System.Diagnostics.Debug.WriteLine("BaseModule---After----写法二");31 }32 private Response MyOnError(NancyContext ctx, Exception ex)33 {34 System.Diagnostics.Debug.WriteLine("BaseModule---OnError----写法二");35 System.Diagnostics.Debug.WriteLine(ex.ToString());36 return null;37 }38 }
在BaseModule中,用了两种不同的形式来对Before、After、OnError进行处理,
都只是打印出一些简单的信息,看这些输出的信息,可以帮助理解内部执行的顺序!
可以看到,Before和OnError是Response类型的,After是void类型的
在这三兄弟的具体处理中,要根据实际情况来定(当然,你想就打印出一些东西也没问题,毕竟我们还是可以把这些东西写进日记嘛)!
下面定义一个HomeModule,具体如下:
1 public class HomeModule : BaseModule2 {3 public HomeModule()4 {5 Get["/"] = _ => "Catcher Wong";6 Get["/err"] = _ => { throw new Exception("there're some errors"); };7 }8 }
其中,当我们访问http://localhost:port时,会显示我们的文字,访问http://localhost:port/err时,会抛出我们设定异常!
运行起来,看看我们的Output(输出)窗口
访问http://localhost:port/err时的情况
出现异常后并没有去执行After!!执行完OnError之后就结束了。
同样的,用写法二也是如此!
基本一致的效果。
用法二:在bootstrapper中重写RequestStartup或者ApplicationStartup
先来看看重写RequestStartup
1 public class Bootstrapper : DefaultNancyBootstrapper 2 { 3 protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) 4 { 5 base.RequestStartup(container, pipelines, context); 6 pipelines.BeforeRequest += ctx => { 7 System.Diagnostics.Debug.WriteLine("Boorstrapper---RequestStartup---Before"); 8 return null; 9 };10 pipelines.AfterRequest += ctx => {11 System.Diagnostics.Debug.WriteLine("Boorstrapper---RequestStartup---After");12 };13 pipelines.OnError += (ctx,ex) => {14 System.Diagnostics.Debug.WriteLine("Boorstrapper---RequestStartup---OnError");15 System.Diagnostics.Debug.WriteLine(ex.ToString());16 return null;17 };18 } 19 }
我们同样是输出相应的信息,运行前,把我们BaseModule中“三兄弟”的注释掉
再来看看重写ApplicationStartup
1 public class Bootstrapper : DefaultNancyBootstrapper 2 { 3 protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) 4 { 5 base.ApplicationStartup(container, pipelines); 6 pipelines.BeforeRequest += MyBeforeRequest; 7 pipelines.AfterRequest += MyAfterRequest; 8 pipelines.OnError += MyOnErroe; 9 }10 private Response MyBeforeRequest(NancyContext ctx)11 {12 System.Diagnostics.Debug.WriteLine("Boorstrapper---ApplicationStartup---Before");13 return null;14 }15 private void MyAfterRequest(NancyContext ctx)16 {17 System.Diagnostics.Debug.WriteLine("Boorstrapper---ApplicationStartup---After");18 }19 private Response MyOnErroe(NancyContext ctx, Exception ex)20 {21 System.Diagnostics.Debug.WriteLine("Boorstrapper---ApplicationStartup---OnError");22 System.Diagnostics.Debug.WriteLine(ex.ToString());23 return null;24 }25 }
我们同样是输出相应的信息,运行前,把我们BaseModule和RequestStartup中“三兄弟”的注释掉
用法三:自定义用法(Nancy中有很多东西可以自定义,这个很灵活,很nice!)
下面来看看自定就要怎么使用!
1 public class CustomRequest : IApplicationStartup 2 { 3 public void Initialize(IPipelines pipelines) 4 { 5 pipelines.BeforeRequest.AddItemToEndOfPipeline(ctx => 6 { 7 System.Diagnostics.Debug.WriteLine("CustomRequest---IApplicationStartup---Before"); 8 return null; 9 });10 pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>11 {12 System.Diagnostics.Debug.WriteLine("CustomRequest---IApplicationStartup---After");13 });14 pipelines.OnError.AddItemToEndOfPipeline((ctx, ex) =>15 {16 System.Diagnostics.Debug.WriteLine("CustomRequest---IApplicationStartup---OnError");17 System.Diagnostics.Debug.WriteLine(ex.ToString());18 return null;19 });20 }21 }
我们自定义一个CustomRequest让它实现IApplicationStartup接口即可!
剩下的就是实现Before、After、OnError的处理!!
把之前的相关处理注释掉,运行。
效果如下:
现在是否很清晰呢?
Before 的执行顺序 IApplicationStartup > ApplicationStartup > RequestStartup > BaseModule
与OnError的处理顺序一样!!
三、内部实现的简单分析
前面也提到了,这三兄弟的实现主要有这几个类
BeforePipeline、AfterPipeline、ErrorPipeline以及抽象类NamedPipelineBase、AsyncNamedPipelineBase
NancyModule中也有相应的Before、After、OnError定义!
先来看看BeforePipeline吧
BeforePipeline是实现了AsyncNamedPipelineBase这个抽象类
里面有用到 implicit operator ,不熟悉的可以参考
有一个重写的Wrap方法,用于把同步的包装成异步的形式
1 protected override PipelineItem>> Wrap(PipelineItem > pipelineItem) 2 { 3 var syncDelegate = pipelineItem.Delegate; 4 Func > asyncDelegate = (ctx, ct) => 5 { 6 var tcs = new TaskCompletionSource (); 7 try 8 { 9 var result = syncDelegate.Invoke(ctx);10 tcs.SetResult(result);11 }12 catch (Exception e)13 {14 tcs.SetException(e);15 }16 return tcs.Task;17 };18 return new PipelineItem >>(pipelineItem.Name, asyncDelegate);19 }
其他的大致都可以总结成下面这句代码:
pipeline.AddItemToEndOfPipeline(xxxx);
把xxxx添加到管道中的末尾去。
同样的,AfterPipeline与ErrorPipeline也是相类似的,
不同的是ErrorPipeline实现的是NamedPipelineBase这个抽象类,
没有那个Wrap方法,多了一个dynamic的Invoke方法
1 public dynamic Invoke(NancyContext context, Exception ex) 2 { 3 dynamic returnValue = null; 4 using (var enumerator = this.PipelineDelegates.GetEnumerator()) 5 { 6 while (returnValue == null && enumerator.MoveNext()) 7 { 8 returnValue = enumerator.Current.Invoke(context, ex); 9 }10 }11 return returnValue;12 }
这个Invoke方法的作用是:依次调用每个管道项目,直到有管道项目被返回或者所有管道项目都已经被调用了!
两个NamePipelineBase(同步和异步)都定义了一个pipelineItems(要执行的管道项目集合)
还有众多虚方法!!大部分是插入的,还有一个删除的。
其中插入可分为在Pipeline的开始和结尾插入,以及是否要替换已存在的同名的Pipeline
下面的是比较重要的一个方法InsertItemAtPipelineIndex
同步的
1 public virtual void InsertItemAtPipelineIndex(int index, PipelineItemitem, bool replaceInPlace = false)2 {3 var existingIndex = this.RemoveByName(item.Name);4 var newIndex = (replaceInPlace && existingIndex != -1) ? existingIndex : index;5 this.pipelineItems.Insert(newIndex, item);6 }
异步的
1 public virtual void InsertItemAtPipelineIndex(int index, PipelineItemitem, bool replaceInPlace = false)2 { 3 var existingIndex = this.RemoveByName(item.Name);4 var newIndex = (replaceInPlace && existingIndex != -1) ? existingIndex : index;5 this.pipelineItems.Insert(newIndex, item);6 }
最后来看看我们在Bootstrapper和自定义用到的IPipelines
1 public interface IPipelines2 { 3 BeforePipeline BeforeRequest { get; set; } 4 AfterPipeline AfterRequest { get; set; }5 ErrorPipeline OnError { get; set; }6 }
十分简单的定义!