In this article, we will discuss about Chain of Responsibility Pattern implementation in Middleware in .NET Core.
The Chain of Responsibility pattern is a behavioral design pattern that allows an object to pass a request along a chain of handlers. Each handler in the chain has the opportunity to process the request or pass it to the next handler in the chain.
The classical implementation using Classes and Interfaces is as shown in the below code
//Chain Of Responsibility
Console.WriteLine("Chain of Responsibility");
var requestNo = 2;
var handler = new Handler1();
var handler2 = new Handler2();
handler.Next = handler2;
Console.WriteLine(handler.HandleRequest(requestNo));
internal class Handler1:Handler
{
protected override int RequestNo { get; set; } = 1;
}
internal class Handler2 : Handler
{
protected override int RequestNo { get; set; } = 2;
}
internal abstract class Handler
{
protected abstract int RequestNo { get; set; }
public Handler? Next { get; set; }
public string HandleRequest(int requestNo)
{
return requestNo == RequestNo ? SuccessMessage(requestNo) : NextMessage(requestNo);
}
protected string SuccessMessage(int requestNo) => $"Handler {requestNo} Handled the request";
protected string NextMessage(int requestNo) => Next?.HandleRequest(requestNo) ?? "No Handler handled the request";
}
However this implementation requires creation of multiple classes and functions and the intent is not clearly shown by the code. To change the imperative implementation to declarative, .NET Core team used delegates. Here is an example to do the same as above code using delegates
//Chain Of Responsibility
Console.WriteLine("Chain of Responsibility");
var chain = new Chain(2);
//handle using delegates like .net core middlewares
chain.Use((requestNo, next) =>
{
if (requestNo == 1) Console.WriteLine($"Handler {requestNo} handled the request");
else
{
Console.WriteLine($"Handler 1 didn't handle the request");
next();
}
});
chain.Use((requestNo, next) =>
{
if (requestNo == 2) Console.WriteLine($"Handler {requestNo} handled the request");
else
{
Console.WriteLine($"Handler 2 didn't handle the request");
next();
}
});
chain.Run(requestNo =>
{
if (requestNo == 3) Console.WriteLine($"Handler {requestNo} Handled the request");
else Console.WriteLine($"No Handler handled the request {requestNo}");
});
public class Chain
{
private readonly int requestNo;
private bool goNext = true;
public Chain(int requestNo)
{
this.requestNo = requestNo;
}
public void Run(Action<int> action)
{
if (goNext)
{
goNext = false;
action(requestNo);
}
}
private void next()
{
goNext = true;
}
public void Use(Action<int, Action> action)
{
if (goNext)
{
goNext = false;
action(requestNo, next);
}
}
}
In the next article we will talk about more design patterns and their advanced implementation.