zoukankan      html  css  js  c++  java
  • 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     }  

    十分简单的定义!

  • 相关阅读:
    systemctl无法停掉keepalived
    python小工具
    python pip
    linux下安装python3
    python process
    python socket模块
    python logging日志模块
    板邓:C#的声明数组和赋值
    板邓:解决jquery中全选点击第二次不生效的问题
    板邓:php+mayql分页原理及案例
  • 原文地址:https://www.cnblogs.com/catcher1994/p/5186915.html
Copyright © 2011-2022 走看看