Nancy之Pipelines三兄弟(Before After OnError)

简介: 原文:Nancy之Pipelines三兄弟(Before After OnError)一、简单描述 Before:如果返回null,拦截器将主动权转给路由;如果返回Response对象,则路由不起作用。
原文: Nancy之Pipelines三兄弟(Before After OnError)

一、简单描述

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 : BaseModule
2     {
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时的情况

 

访问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的执行顺序 BaseModule > IApplicationStartup > ApplicationStartup > RequestStartup
 
再来看看After的执行顺序

与OnError的处理顺序一样!!

三、内部实现的简单分析

前面也提到了,这三兄弟的实现主要有这几个类

BeforePipeline、AfterPipeline、ErrorPipeline以及抽象类NamedPipelineBase、AsyncNamedPipelineBase

NancyModule中也有相应的Before、After、OnError定义!

先来看看BeforePipeline吧

BeforePipeline是实现了AsyncNamedPipelineBase这个抽象类

里面有用到 implicit operator ,不熟悉的可以参考

implicit (C# Reference)

有一个重写的Wrap方法,用于把同步的包装成异步的形式

 1         protected override PipelineItem<Func<NancyContext, CancellationToken, Task<Response>>> Wrap(PipelineItem<Func<NancyContext, Response>> pipelineItem)
 2         {
 3             var syncDelegate = pipelineItem.Delegate;
 4             Func<NancyContext, CancellationToken, Task<Response>> asyncDelegate = (ctx, ct) =>
 5             {
 6                 var tcs = new TaskCompletionSource<Response>();
 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<Func<NancyContext, CancellationToken, Task<Response>>>(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, PipelineItem<TDelegate> item, 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, PipelineItem<TAsyncDelegate> item, 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         }  

 

这个方法的主要作用是将item插入到Pipeline的指定位置!(同步和异步的都有相应的实现!!不同的是item的类型而已!)
 
内部实现,简单点的说法就是:就把我们写的东西添加进Pipline去处理
 

 最后来看看我们在Bootstrapper和自定义用到的IPipelines

1     public interface IPipelines
2     {   
3         BeforePipeline BeforeRequest { get; set; }     
4         AfterPipeline AfterRequest { get; set; }
5         ErrorPipeline OnError { get; set; }
6     }  

十分简单的定义!

 

目录
相关文章
|
8月前
|
前端开发
第二十四章 案例TodoList 之props限制
第二十四章 案例TodoList 之props限制
|
机器人
hdu1035 Robot Motion(简单模拟题)
hdu1035 Robot Motion(简单模拟题)
49 0
UIAlertController简单使用
UIAlertController简单使用
132 0
UIAlertController简单使用
|
前端开发 JavaScript
Components make life easier(组件复用让生活更美好)
Components and Props Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.
99 0
|
JavaScript 前端开发 Python
《跟热饭一起学习vue吧》Part.8 v-on监听和methods函数仓库
《跟热饭一起学习vue吧》Part.8 v-on监听和methods函数仓库
《跟热饭一起学习vue吧》Part.8 v-on监听和methods函数仓库
|
JavaScript
《跟热饭一起学习vue吧》Part.18 事件修饰符
《跟热饭一起学习vue吧》Part.18 事件修饰符
《跟热饭一起学习vue吧》Part.18 事件修饰符
|
JavaScript
《跟热饭一起学习vue吧》Part.22 组件带参数 Prop
《跟热饭一起学习vue吧》Part.22 组件带参数 Prop
《跟热饭一起学习vue吧》Part.22 组件带参数 Prop
|
JavaScript 前端开发 Python
《跟热饭一起学习vue吧》Part.13 v-for循环
《跟热饭一起学习vue吧》Part.13 v-for循环
《跟热饭一起学习vue吧》Part.13 v-for循环
|
前端开发 JavaScript 程序员
造一个 react-error-boundary 轮子
朋友们好啊,我是海怪,刚才老板对我说:海怪,发生甚么事了,怎么页面白屏了?我说:怎么回事?给我发了几张截图。我打开控制台一看:哦!原来是昨天,有个后端年轻人,说要和我联调接口,我说:可以。然后,我说:小兄弟,你的数据尽量按我需要的格式来:踏不服气,他说你这个没用,我说我这个有用,这是规范,传统前后端联调返回数据是要讲规范的,对项目质量的提高可以起到四两拨千斤的作用。100多万行代码的系统,只要有了类型规范,都不会轻易崩溃。他说试试,我说行。 我请求刚发出去,他的数据,啪!的一下就返回了!很快啊!!
造一个 react-error-boundary 轮子
|
Web App开发 前端开发 JavaScript