前一篇文章「練習題 - 設計模式 - 責任鍊模式 (職責鍊模式) Chain of Responsibility Pattern」是不藉助任何套件的方式下完成實作,但我總會想應該是有人會做個 NuGet 套件來讓開發者可以直接套用就能夠完成責任鍊模式的實作,找了一輪後發現到還蠻少的,雖然少但也還是有一些,不過有的是已經很久沒有更新或是用的人不多,而有些是不適合真的拿來使用,有這種情況大概是責任鍊模式的實作其實並不複雜。
不過還是有找到一個還可以的套件,所以就拿來用在之前的練習題裡,看看是不是適合。
PipelineNet
PipelineNet 是一個微型框架,幫助你實現管道(Pipeline)和責任鍊(Chain of Responsibility)模式。使用 PipelineNet,你可以輕鬆地將業務邏輯分離,並擴展你的應用程式。管道可以用來依序執行一系列的中介軟體(middleware),而不期望有回傳值;而責任鍊則做相同的事,但期望有回傳值。此外,這些操作也都可以以非同步方式進行。
這邊先不說明 套件的 Pipeline 功能,直接看責任鍊(Chains of Responsibility)模式的功能支援。

責任鍊模式有兩種實作方式:ResponsibilityChain<TParameter, TReturn> 和 AsyncResponsibilityChain<TParameter, TReturn>。這兩者具有相同的功能,都能聚合並執行一系列的 middleware,並回傳一個結果類型。
責任鍊與管道(pipeline)的差別是可以使用 Finally 方法來定義 fallback function。可以為責任鍊設置一個 fallback function,如果多次調用該方法,則會覆蓋之前定義的 function。
使用 PipelineNet
建立各個 Handler 類別並繼承實作 PipeneNet 的 IAsyncMiddleware<TParameter,TReturn> 介面,而這邊方法的回傳型別還是一樣使用 HandlerResult
/// <summary>
/// class TokenExistHandler
/// </summary>
public class TokenExistHandler : IAsyncMiddleware<string, HandlerResult>
{
/// <summary>
/// Runs the parameter
/// </summary>
/// <param name="parameter">The parameter</param>
/// <param name="next">The next</param>
/// <returns>A task containing the handler result</returns>
public async Task<HandlerResult> Run(string parameter, Func<string, Task<HandlerResult>> next)
{
if (string.IsNullOrEmpty(parameter))
{
return new HandlerResult(
isSuccessful: false,
actionResult: new UnauthorizedObjectResult("Token is missing."));
}
return await next(parameter);
}
}
上一篇的實作裡是要自己建立一個 IHandler 介面和 HandlerBase 抽象類別,然後各個 Handler 去繼承實作。而使用 PipelineNet 的話就看你的實作是否有用到非同步,如果有的話就繼承實作 IAsyncMiddleware<TParameter,TReturn> 介面,沒有非同步就繼承實作 IMiddleware<TParameter,TReturn> 介面。
這邊就不再一一展示各個 Handler 實作的程式碼。
接著就如同 PipelineNet 的範例,要建立 IResponsibilityHandlerFactory 介面與 ResponsibilityHandlerFactory 類別,主要的工作串起每個 Handler 形成執行責任鍊。

/// <summary>
/// interface IResponsibilityHandlerFactory
/// </summary>
public interface IResponsibilityHandlerFactory
{
/// <summary>
/// Creates the responsibility chain
/// </summary>
/// <returns>An async responsibility chain of string and handler result</returns>
IAsyncResponsibilityChain<string, HandlerResult> CreateResponsibilityChain();
}
而我們這邊的 ResponsibilityHandlerFactory 類別會使用到 Finally 方法去定義 fallback function,這是定義當職責鍊裡每個 Handler 執行結果的 IsSuccessful 都為 true 時最後要回傳的結果,上一篇的這個處理是寫在 HandlerBase 抽象類別裡,

但這次是使用 PipelineNet 就不必用到 HandlerBase,直接使用 IAsyncResponsibilityChain<string, HandlerResult> 的 Finally 方法來設定這個處理,以下是 ResponsibilityHandlerFactory 類別的實作內容
using PipelineNet.ChainsOfResponsibility;
using PipelineNet.ServiceProvider.MiddlewareResolver;
using WebApplication1.Models;
namespace WebApplication1.PipelineNet;
/// <summary>
/// class ResponsibilityHandlerFactory
/// </summary>
public class ResponsibilityHandlerFactory : IResponsibilityHandlerFactory
{
private readonly IServiceProvider _serviceProvider;
/// <summary>
/// Initializes a new instance of the <see cref="ResponsibilityHandlerFactory"/> class
/// </summary>
/// <param name="serviceProvider">The service provider</param>
public ResponsibilityHandlerFactory(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
/// <summary>
/// Creates the responsibility chain
/// </summary>
/// <returns>An async responsibility chain of string and handler result</returns>
public IAsyncResponsibilityChain<string, HandlerResult> CreateResponsibilityChain()
{
return new AsyncResponsibilityChain<string, HandlerResult>(new ActivatorUtilitiesMiddlewareResolver(this._serviceProvider))
.Chain<TokenExistHandler>()
.Chain<TokenValidationHandler>()
.Chain<TokenExpirationHandler>()
.Chain<RoleCheckHandler>()
.Chain<TransactionLimitHandler>()
.Chain<ExternalServiceHandler>()
.Finally(_ => Task.FromResult(new HandlerResult(true)));
}
}
對了,這邊會使用到 ActivatorUtilitiesMiddlewareResolver 這個類別,這個類別是另外一個 NuGet 套件「PipelineNet.ServiceProvider」裡,所以要記得除了 PipelineNet 之外也需要安裝 PipelineNet.ServiceProvider

Program.cs 的註冊 IResponsibilityHandlerFactory 與 ResponsibilityHandlerFactory

接著是 Controller 裡相依注入使用 IResponsibilityHandlerFactory,在 Action 方法裡使用 _responsibilityHandlerFactory.CreateResponsibilityChain() 方法建立責任鍊,然後使用 chain.Execute 方法去執行責任鍊的處理
/// <summary>
/// class ExampleWithPipelineNetController
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class ExampleWithPipelineNetController : ControllerBase
{
private readonly IResponsibilityHandlerFactory _responsibilityHandlerFactory;
/// <summary>
/// Initializes a new instance of the <see cref="ExampleWithPipelineNetController"/> class
/// </summary>
/// <param name="responsibilityHandlerFactory">The responsibilityHandlerFactory</param>
public ExampleWithPipelineNetController(IResponsibilityHandlerFactory responsibilityHandlerFactory)
{
this._responsibilityHandlerFactory = responsibilityHandlerFactory;
}
[HttpGet("data")]
public async Task<IActionResult> GetDataAsync([FromHeader] string token)
{
var chain = this._responsibilityHandlerFactory.CreateResponsibilityChain();
var result = await chain.Execute(token);
// 如果處理不成功,返回對應的錯誤結果
if (!result.IsSuccessful)
{
return result.ActionResult;
}
// 所有處理成功,回傳最終成功結果
return this.Ok("Success: All validations passed.");
}
}
這樣就完成了使用 PipelineNet 實作責任鍊模式。
其實經過實作後覺得好像跟原本不用套件的方式並沒有差太多,使用 PipelineNet 的實作是可以少建立幾個介面與抽象類別,對於一個專案裡只會有一個責任鍊需求的情況來說,不用套件也沒有什麼差別,但如果一個專案裡會實作許多責任鍊的情況時,使用 PipelineNet 就會方便不少。

以上
純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力