Skip to content

Instantly share code, notes, and snippets.

@zhangr4
Last active March 9, 2023 08:21
Show Gist options
  • Save zhangr4/153bcfbbcd38a7fdb589c740f8dd8022 to your computer and use it in GitHub Desktop.
Save zhangr4/153bcfbbcd38a7fdb589c740f8dd8022 to your computer and use it in GitHub Desktop.
a simple implementation like asp.net middleware pipeline
/*
* just for learning
* key concept, LinkedList
* refer https://blog.csdn.net/qq_16587307/article/details/104301877
* refer https://medium.com/@bonnotguillaume/software-architecture-the-pipeline-design-pattern-from-zero-to-hero-b5c43d8a4e60
*/
namespace pipeline
{
public class Program
{
static void Main()
{
Context context = new ConcreteContext();
context.Name = "test context";
var pipelineBuilder = new PipelineBuilder();
pipelineBuilder.AddHandler(new ConcreteHandler(name: "hanlder1"));
pipelineBuilder.AddHandler(new ConcreteHandler(name: "handler2"));
var pipeline = pipelineBuilder.Build();
pipeline.Invoke(context);
Console.ReadLine();
}
}
public abstract class Context
{
public virtual string Name { get; set; } = string.Empty;
}
public class ConcreteContext : Context
{
public override string Name { get; set; } = string.Empty;
}
public interface IHandler
{
public Task Invoke(Context context);
public void SetNext(IHandler handler);
}
public class ConcreteHandler : IHandler
{
private string _name = string.Empty;
public ConcreteHandler() { }
public ConcreteHandler(string name)
{
_name = name;
}
private IHandler? _next; //maintain next handler in handler its self or in pipeline
public void SetNext(IHandler handler)
{
_next = handler;
}
public async Task Invoke(Context context)
{
//do something before
Console.WriteLine($"enter handler [{_name}], address context [{context.Name}] and pass to next handler");
await _next!.Invoke(context);
Console.WriteLine($"exit handler [{_name}]");
//do something after
}
}
public class DefaultHandler : IHandler
{
public Task Invoke(Context context)
{
//do nothing
Console.WriteLine("default");
return Task.CompletedTask;
}
public void SetNext(IHandler handler)
{
}
}
public class PipelineBuilder
{
private IList<IHandler> _handlers = new List<IHandler>();
public void AddHandler(IHandler handler)
{
_handlers.Add(handler);
}
public IHandler Build()
{
IHandler endpoint = new DefaultHandler();
foreach (var handler in _handlers.Reverse())
{
handler.SetNext(endpoint);
endpoint = handler;
}
return endpoint;
}
}
}
namespace Pipeline
{
public class HttpContext
{
public string Request { get; set; }
public string Response { get; set; }
}
public delegate Task RequestDelegate(HttpContext context);
public interface IApplicationBuilder
{
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
RequestDelegate Build();
}
public class ApplicationBuilder : IApplicationBuilder
{
private readonly IList<Func<RequestDelegate, RequestDelegate>> _components
= new List<Func<RequestDelegate, RequestDelegate>>();
public RequestDelegate Build()
{
RequestDelegate app = async ctx =>
{
Console.WriteLine("deault middleware");
await Task.CompletedTask;
};
foreach (var component in _components.Reverse())
{
app = component(app);
}
return app;
}
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
}
public static class Extensions
{
public static void Run(this IApplicationBuilder appbuilder, RequestDelegate handler)
{
if (appbuilder == null)
{
throw new ArgumentNullException("app");
}
if (handler == null)
{
throw new ArgumentNullException();
}
appbuilder.Use(_ => handler);
}
public static IApplicationBuilder Use(
this IApplicationBuilder appbuilder, Func<HttpContext, Func<Task>, Task> middleware)
{
return appbuilder.Use(next =>
{
return context =>
{
Func<Task> simpleNext = () => next(context);
return middleware(context, simpleNext);
};
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment