讓 .NET 輕松構(gòu)建中間件模式代碼(二)--- 支持管道的中斷和分支
Intro
上次實現(xiàn)了一個基本的構(gòu)建中間件模式的中間件構(gòu)建器,現(xiàn)在來豐富一下功能,讓它支持中斷和分支,分別對應(yīng) asp.net core 中的 applicationBuilder.Run 和 applicationBuilder.MapWhen
實現(xiàn)管道中斷
實現(xiàn)中間件的中斷其實很簡單,通過上一次的分析我們已經(jīng)知道,中間件每一個部分其實是一個上下文和 next 的委托,只需要忽略 next,不執(zhí)行 next 就可以了,就可以中斷后面中間件的執(zhí)行。
定義一個 Run 擴(kuò)展方法來實現(xiàn)方便的實現(xiàn)中間件中斷:
public static IPipelineBuilder<TContext> Run<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext> handler)
{
return builder.Use(_ => handler);
}
public static IAsyncPipelineBuilder<TContext> Run<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, Task> handler)
{
return builder.Use(_ => handler);
}
實現(xiàn)分支
分支的實現(xiàn)主要是參考 asp.net core 里 applicationBuilder.Map/applicationBuilder.MapWhen 實現(xiàn)分支路由的做法,在 asp.net core 里,MapWhen 是一個擴(kuò)展方法,其實現(xiàn)是一個 MapWhenMiddleware,有興趣可以看 asp.net core 的源碼。
實現(xiàn)原理也挺簡單的,其實就是滿足分支的條件時創(chuàng)建一個全新的中間件管道,當(dāng)滿足條件的時候就就執(zhí)行這個分支中間件管道,否則就跳過這個分支進(jìn)入下一個中間件。
首先在 PipelineBuilder 的接口定義中增加了一個 New 方法用來創(chuàng)建一個全新的中間件管道,定義如下:
public interface IPipelineBuilder<TContext>
{
IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware);
Action<TContext> Build();
IPipelineBuilder<TContext> New();
}
//
public interface IAsyncPipelineBuilder<TContext>
{
IAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, Task>, Func<TContext, Task>> middleware);
Func<TContext, Task> Build();
IAsyncPipelineBuilder<TContext> New();
}
實現(xiàn)就是直接創(chuàng)建了一個新的 PipelineBuilder<TContext> 對象,示例如下:
internal class PipelineBuilder<TContext> : IPipelineBuilder<TContext>
{
private readonly Action<TContext> _completeFunc;
private readonly List<Func<Action<TContext>, Action<TContext>>> _pipelines = new List<Func<Action<TContext>, Action<TContext>>>();
public PipelineBuilder(Action<TContext> completeFunc)
{
_completeFunc = completeFunc;
}
public IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware)
{
_pipelines.Add(middleware);
return this;
}
public Action<TContext> Build()
{
var request = _completeFunc;
for (var i = _pipelines.Count - 1; i >= 0; i--)
{
var pipeline = _pipelines[i];
request = pipeline(request);
}
return request;
}
public IPipelineBuilder<TContext> New() => new PipelineBuilder<TContext>(_completeFunc);
}
異步的和同步類似,這里就不再贅述,有疑問可以直接看文末的源碼鏈接
接著就可以定義我們的分支擴(kuò)展了
public static IPipelineBuilder<TContext> When<TContext>(this IPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IPipelineBuilder<TContext>> 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<RequestContext>(context =>
{
Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed");
})
.When(context => context.Hour <= 2, pipeline =>
{
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 <= 4, pipeline =>
{
pipeline.Run(context => Console.WriteLine("pass 2"));
})
.When(context => context.Hour <= 6, pipeline =>
{
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("----------------------------");
}
輸出結(jié)果如下:

看輸出結(jié)果我們可以看到 Run 后面注冊的中間件是不會執(zhí)行的,Run 前面注冊的中間件正常執(zhí)行
然后定義的 When 分支也是正確執(zhí)行的~~
Reference
|