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

在此模式中抽象了一個類似管道的概念,所有的組件均以中間件的方式注冊在此管道中,當請求進入管道后:中間件依次對請求作出處理,然后從最后一個中間件開始處理響應內容,最終反向流出管道。
二、契約式設計
契約式設計是從面向對象的角度來思考問題,根據管道-中間件的理解,中間件(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);
}
實現(xiàn)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并對消息的請求和響應進行處理,最后返回最終處理過的消息。
接下來需要實現(xiàn)一個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;
????
}
}
編寫測試:

三、函數(shù)式設計方案
此方案也是Owin和ASP.NET Core采用的方案,如果站在面向對象的角度,第一個方案是非常清晰的,管道最終通過枚舉所有Middleware來依次處理請求。
站在函數(shù)式的角度來看,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;
}
這一過程是理解函數(shù)式方案的關鍵,所有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);
}
自定義一個函數(shù)式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;
????
}
}