ASP.NET Core 1.0借鑑了Katana項目的管道設計(Pipeline)。日誌記錄、用戶認證、MVC等模塊都以中間件(Middleware)的方式註冊在管道中。顯而易見這樣的設計很是鬆耦合而且很是靈活,你能夠本身定義任意功能的Middleware註冊在管道中。這一設計很是適用於「請求-響應」這樣的場景——消息從管道頭流入最後反向流出。html
在本文中暫且爲這種模式起名叫作「管道-中間件(Pipeline-Middleware)」模式吧。git
本文將描述」管道-中間件模式」的「契約式」設計和「函數式」設計兩種方案。app
1、什麼是管道-中間件模式?函數
在此模式中抽象了一個相似管道的概念,全部的組件均以中間件的方式註冊在此管道中,當請求進入管道後:中間件依次對請求做出處理,而後從最後一箇中間件開始處理響應內容,最終反向流出管道。測試
2、契約式設計ui
契約式設計是從面向對象的角度來思考問題,根據管道-中間件的理解,中間件(Middleware)有兩個職責:this
public interface IMiddleware { Request ProcessRequest(Request request); Response ProcessResponse(Response response); }
管道(Pipeline)抽象應該可以註冊中間件(Middleware):.net
public interface IApplicationBuilder { void Use(IMiddleware middleware); void UseArrange(List<IMiddleware> middlewares); Context Run(Context context); }
實現IApplicationBuilder:設計
public class ApplicationBuilder : IApplicationBuilder { public IWindsorContainer Container { get; private set; } private readonly List<IMiddleware> _middlewares; public ApplicationBuilder(IWindsorContainer container) { Contract.Requires(container!=null,"container!=null"); _middlewares=new List<IMiddleware>(); Container = container; } public void Use(IMiddleware middleware) { Contract.Requires(middleware != null, "middleware!=null"); _middlewares.Add(middleware); } public void UseArrange(List<IMiddleware> middlewares) { Contract.Requires(middlewares != null, "middlewares!=null"); _middlewares.AddRange(middlewares); } public Context Run(Context context) { Contract.Requires(context!=null,"context!=null"); var request=context.Request; var response=context.Response; foreach (var middleware in _middlewares) { request = middleware.ProcessRequest(request); } _middlewares.Reverse(); foreach (var middleware in _middlewares) { response = middleware.ProcessResponse(response); } return new Context(request,response); } }
Run()方法將依次枚舉Middleware並對消息的請求和響應進行處理,最後返回最終處理過的消息。日誌
接下來須要實現一個Middleware:
public class DefaultMiddleware:IMiddleware { public Request ProcessRequest(Request request) { request.Process("default request", "processed by defaultMiddleware"); return request; } public Response ProcessResponse(Response response) { response.Process("default response", "processed by defaultMiddleware"); return response; } }
爲了將Middleware註冊進管道,咱們還能夠寫一個擴展方法增長代碼的可讀性:
public static void UseDefaultMiddleware(this IApplicationBuilder applicationBuilder) { applicationBuilder.Use<DefaultMiddleware>(); } public static void Use<TMiddleware>(this IApplicationBuilder applicationBuilder) where TMiddleware:IMiddleware { var middleware = applicationBuilder.Container.Resolve<TMiddleware>(); applicationBuilder.Use(middleware); }
寫個測試看看吧:
寫第二個Middleware:
public class GreetingMiddleware:IMiddleware { public Request ProcessRequest(Request request) { request.Process("hello, request","processed by greetingMiddleware"); return request; } public Response ProcessResponse(Response response) { response.Process("hello, request", "processed by greetingMiddleware"); return response; } }
編寫測試:
3、函數式設計方案
此方案也是Owin和ASP.NET Core採用的方案,若是站在面向對象的角度,第一個方案是很是清晰的,管道最終經過枚舉全部Middleware來依次處理請求。
站在函數式的角度來看,Middleware能夠用Func<Context, Context>來表示,再來看看這張圖:
一個Middleware的邏輯能夠用Func<Func<Context, Context>, Func<Context, Context>>來表示,整個Middleware的邏輯能夠用下面的代碼描述:
public Func<Func<Context, Context>, Func<Context, Context>> Process() { Func<Func<Context, Context>, Func<Context, Context>> middleware = next => { Func<Context, Context> process = context => { /*process request*/ next(context); /*process response*/ return context; }; return process; }; return middleware; }
這一過程是理解函數式方案的關鍵,全部Middleware能夠聚合爲一個Func<Context,Context>,爲了易於閱讀,咱們能夠定義一個委託:
public delegate Context RequestDelegate(Context context);
給定初始RequestDelegate,聚合全部Middleware:
public IApplication Build() { RequestDelegate request = context => context; _middlewares.Reverse(); foreach (var middleware in _middlewares) { request = middleware(request); } return new Application(request); }
自定義一個函數式Middleware:
public class DefaultMiddleware:IMiddleware { public Func<RequestDelegate, RequestDelegate> Request() { Func<RequestDelegate, RequestDelegate> request = next => { return context => { context.Request.Process("default request", "processed by defaultMiddleware"); next(context); context.Response.Process("default response", "processed by defaultMiddleware"); return context; }; }; return request; } }
全部代碼提供下載:https://git.oschina.net/richieyangs/Pipeline.Middleware.git