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     }  

十分简单的定义!

 

目录
相关文章
|
存储 编译器 C++
关于“VS2022无法打开头文件<graphics.h>” 以及编译时 “没有与参数列表匹配的重载函数实例”俩个问题的解决思路
关于“VS2022无法打开头文件<graphics.h>” 以及编译时 “没有与参数列表匹配的重载函数实例”俩个问题的解决思路
4282 0
|
1月前
|
人工智能 运维 BI
Top5 主流工单管理系统全对比(2025 版):功能、价格、行业适配性详解
在数字化浪潮推动下,工单管理系统已成为企业提升运营效率、优化客户体验的关键工具。本文解析其核心价值与选型要点,并对合力亿捷、Zendesk、Freshdesk、Jira Service Management、钉钉宜搭五大主流系统进行多维度对比,涵盖功能、价格、行业适配性等,助力企业精准选型,加速数字化转型进程。
|
云安全 安全 小程序
无影-阿里云第一款云电脑,它拥有超越PC的完美体验
无影是一款面向数字经济时代的生产力工具,基于流式传输服务和容器化架构,可实现随时随地云上办公、海量算力触手可得、海量应用一网打尽,依托阿里云打造云管端一体化安全防护体系,全面保障企业业务和数据安全,拥有超越PC的便捷、流畅、安全、高效体验。
27350 0
无影-阿里云第一款云电脑,它拥有超越PC的完美体验
|
6月前
|
安全 Linux 虚拟化
VMware Tools 12.5.1 下载 - 虚拟机必备组件 (驱动和交互式服务)
虚拟机必备组件 (驱动和交互式服务)
2293 14
VMware Tools 12.5.1 下载 - 虚拟机必备组件 (驱动和交互式服务)
|
10月前
|
人工智能 JSON 算法
Qwen2.5-Coder 系列模型在 PAI-QuickStart 的训练、评测、压缩及部署实践
阿里云的人工智能平台 PAI,作为一站式、 AI Native 的大模型与 AIGC 工程平台,为开发者和企业客户提供了 Qwen2.5-Coder 系列模型的全链路最佳实践。本文以Qwen2.5-Coder-32B为例,详细介绍在 PAI-QuickStart 完成 Qwen2.5-Coder 的训练、评测和快速部署。
Qwen2.5-Coder 系列模型在 PAI-QuickStart 的训练、评测、压缩及部署实践
|
SQL 资源调度 关系型数据库
实时计算 Flink版产品使用合集之在抓取 MySQL binlog 数据时,datetime 字段会被自动转换为时间戳形式如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
197 2
|
消息中间件 Kubernetes API
在K8S中,如何收集k8s集群日志?
在K8S中,如何收集k8s集群日志?
|
存储 监控 NoSQL
Celery是一个基于分布式消息传递的异步任务队列/作业队列
Celery是一个基于分布式消息传递的异步任务队列/作业队列
halcon联合c#、WPF学习笔记二(简单案例)
halcon联合c#、WPF学习笔记二(简单案例)
1081 0
|
存储 消息中间件 资源调度
什么是PaaS平台
PaaS平台通常是基于IaaS平台构建的,PaaS平台和IaaS平台最大的差别是需求即服务。所有的管理都是以服务为粒度的,在IaaS以资源管理为中心的平台上提供了更高层次的抽象。
2997 2