Let .NET easily build middleware pattern code (2)

Let .NET easily build middleware pattern code (2) --- support pipeline interruption and branching

Intro#

Last implements a basic building middleware middleware model builder, now look rich functionality, it supports interrupt and branches, respectively in asp.net core  applicationBuilder.Run and applicationBuilder.MapWhen

Implement pipeline interruption #

Realizing the interruption of middleware is actually very simple. Through the last analysis, we already know that each part of the middleware is actually a context and  next a delegate. It only needs to be ignored  nextand not executed  next , and the execution of the middleware can be interrupted.

Define an  Run extension method to implement the middleware interrupt conveniently:

Copy
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); } 

Implement branch #

The implementation of the branch is mainly  based on the practice of asp.net core  applicationBuilder.Map/ applicationBuilder.MapWhenimplementation of branch routing. In asp.net core, it MapWhen is an extension method, and its implementation is one  MapWhenMiddleware. If you are interested, you can see the source code of asp.net core.

The implementation principle is also quite simple. In fact, a brand new middleware pipeline is created when the condition of the branch is met. When the condition is met, the branch middleware pipeline is executed, otherwise the branch is skipped and the next middleware is skipped.

First  PipelineBuilder , a New method was added to the interface definition  to create a brand new middleware pipeline, which is defined as follows:

Copy
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(); } 

The realization is to directly create a new  PipelineBuilder<TContext> object, examples are as follows:

Copy
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); } 

Asynchronous is similar to synchronous, so I wo n’t go into details here. If you have any questions, you can directly see the source link at the end of the article.

Then we can define our branch extension

Copy
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(); } }); } 

Example of use #

We can use branches and interrupts to transform yesterday's example. The modified example is as follows:

Copy
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("----------------------------"); } 

The output is as follows:

Looking at the output, we can see that  Run the middleware registered later will not be executed, and Run the middleware registered before will execute normally

Then the defined  When branch is also executed correctly ~~

Reference#

Guess you like

Origin www.cnblogs.com/guoguo251/p/12709943.html