ASP.NET Core 1.0借鑒了Katana項目的管道設計(Pipeline)。日志記錄、用戶認證、MVC等模塊都以中間件(Middleware)的方式注冊在管道中。顯而易見這樣的設計非常松耦合並且非常靈活,你可以自己定義任意功能的Middleware注冊在管道中。這一設計非常適用於“請求-響應”這樣的場景——消息從管道頭流入最后反向流出。
在本文中暫且為這種模式起名叫做“管道-中間件(Pipeline-Middleware)”模式吧。
本文將描述”管道-中間件模式”的“契約式”設計和“函數式”設計兩種方案。
一、什么是管道-中間件模式?

在此模式中抽象了一個類似管道的概念,所有的組件均以中間件的方式注冊在此管道中,當請求進入管道后:中間件依次對請求作出處理,然后從最后一個中間件開始處理響應內容,最終反向流出管道。
二、契約式設計
契約式設計是從面向對象的角度來思考問題,根據管道-中間件的理解,中間件(Middleware)有兩個職責:
public interface IMiddleware
{
Request ProcessRequest(Request request);
Response ProcessResponse(Response response);
}
管道(Pipeline)抽象應該能夠注冊中間件(Middleware):
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;
}
}
編寫測試:

三、函數式設計方案
此方案也是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
