标签:上下文 代码 bst lis pip ddl 中间件 private com
让 .NET 轻松构建中间件模式代码(二)--- 支持管道的中断和分支
Intro
上次实现了一个基本的构建中间件模式的中间件构建器,现在来丰富一下功能,让它支持中断和分支,分别对应 asp.net core 中的 applicationBuilder.Run
和 applicationBuilder.MapWhen
实现管道中断
实现中间件的中断其实很简单,通过上一次的分析我们已经知道,中间件每一个部分其实是一个上下文和 next
的委托,只需要忽略 next
,不执行 next
就可以了,就可以中断后面中间件的执行。
定义一个 Run
扩展方法来实现方便的实现中间件中断:
public static IPipelineBuilder Run(this IPipelineBuilder builder, Action handler)
{
return builder.Use(_ => handler);
}
public static IAsyncPipelineBuilder Run(this IAsyncPipelineBuilder builder, Func handler)
{
return builder.Use(_ => handler);
}
实现分支
分支的实现主要是参考 asp.net core 里 applicationBuilder.Map
/applicationBuilder.MapWhen
实现分支路由的做法,在 asp.net core 里,MapWhen
是一个扩展方法,其实现是一个 MapWhenMiddleware
,有兴趣可以看 asp.net core 的源码。
实现原理也挺简单的,其实就是满足分支的条件时创建一个全新的中间件管道,当满足条件的时候就就执行这个分支中间件管道,否则就跳过这个分支进入下一个中间件。
首先在 PipelineBuilder
的接口定义中增加了一个 New
方法用来创建一个全新的中间件管道,定义如下:
public interface IPipelineBuilder
{
IPipelineBuilder Use(Func, Action> middleware);
Action Build();
IPipelineBuilder New();
}
//
public interface IAsyncPipelineBuilder
{
IAsyncPipelineBuilder Use(Func, Func> middleware);
Func Build();
IAsyncPipelineBuilder New();
}
实现就是直接创建了一个新的 PipelineBuilder
对象,示例如下:
internal class PipelineBuilder : IPipelineBuilder
{
private readonly Action _completeFunc;
private readonly List, Action>> _pipelines = new List, Action>>();
public PipelineBuilder(Action completeFunc)
{
_completeFunc = completeFunc;
}
public IPipelineBuilder Use(Func, Action> middleware)
{
_pipelines.Add(middleware);
return this;
}
public Action Build()
{
var request = _completeFunc;
for (var i = _pipelines.Count - 1; i >= 0; i--)
{
var pipeline = _pipelines[i];
request = pipeline(request);
}
return request;
}
public IPipelineBuilder New() => new PipelineBuilder(_completeFunc);
}
异步的和同步类似,这里就不再赘述,有疑问可以直接看文末的源码链接
接着就可以定义我们的分支扩展了
public static IPipelineBuilder When(this IPipelineBuilder builder, Func predict, Action> configureAction)
{
return builder.Use((context, next) =>
{
if (predict.Invoke(context))
{
var branchPipelineBuilder = builder.New();
configureAction(branchPipelineBuilder);
var branchPipeline = branchPipelineBuilder.Build();
branchPipeline.Invoke(context);
}
else
{
next();
}
});
}
使用示例
我们可以使用分支和中断来改造一下昨天的示例,改造完的示例如下:
var requestContext = new RequestContext()
{
RequesterName = "Kangkang",
Hour = 12,
};
var builder = PipelineBuilder.Create(context =>
{
Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed");
})
.When(context => context.Hour
{
pipeline.Use((context, next) =>
{
Console.WriteLine("This should be invoked");
next();
});
pipeline.Run(context => Console.WriteLine("pass 1"));
pipeline.Use((context, next) =>
{
Console.WriteLine("This should not be invoked");
next();
Console.WriteLine("will this invoke?");
});
})
.When(context => context.Hour
{
pipeline.Run(context => Console.WriteLine("pass 2"));
})
.When(context => context.Hour
{
pipeline.Run(context => Console.WriteLine("pass 3"));
})
;
var requestPipeline = builder.Build();
Console.WriteLine();
foreach (var i in Enumerable.Range(1, 8))
{
Console.WriteLine($"--------- h:{i} apply Pipeline------------------");
requestContext.Hour = i;
requestPipeline.Invoke(requestContext);
Console.WriteLine("----------------------------");
}
输出结果如下:
看输出结果我们可以看到 Run
后面注册的中间件是不会执行的,Run
前面注册的中间件正常执行
然后定义的 When
分支也是正确执行的~~
Reference
- https://www.cnblogs.com/weihanli/p/12700006.html
- https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/DotNetCoreSample/PipelineTest.cs
- https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/Pipelines/PipelineBuilder.cs
- https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http/src/Builder/ApplicationBuilder.cs
- https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http.Abstractions/src/Extensions/MapWhenExtensions.cs
- https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http.Abstractions/src/Extensions/MapWhenMiddleware.cs
让 .NET 轻松构建中间件模式代码(二)
标签:上下文 代码 bst lis pip ddl 中间件 private com
原文地址:https://www.cnblogs.com/weihanli/p/12709603.html