中間件是一類裝配在應用管道的代碼,負責處理請求和響應。每個中間件都可在管道中的下一個組件前後執行工作,並選擇是否將請求傳遞到管道中的下一個中間件。在Startup.Configure方法中可以進行中間件的裝配。
中間件管道模型
中間件管道模型如下圖所示:
ASP.NET Core請求管道包含一系列請求委託,沿黑色箭頭依次被調用執行,每個委託均可在下一個委託前後執行操作。這種模型也被形象地稱為“俄羅斯套娃”。
一箇中間件可以是匿名方法的顯示嵌入到管道中,也可以封裝為單獨的類便於重用,嵌入式的中間件就像這樣:
<code>public
void
Configure
(IApplicationBuilder app
) { app.Run(async
context => {await
context.Response.WriteAsync("Hello, World!"
); }); } /<code>
中間件的配置
配置中間件會使用到三個擴展方法:
- Use
- Run
- Map
Use
Use用來將多箇中間件按添加順序鏈接到一起:
<code>app.Use(async
(context, next) => {await
context.Response.WriteAsync("middleware1 begin\r\n"
);await
next.Invoke();await
context.Response.WriteAsync("middleware1 end\r\n"
); }); app.Use(async
(context, next) => {await
context.Response.WriteAsync("middleware2 begin\r\n"
);await
next.Invoke();await
context.Response.WriteAsync("middleware2 end\r\n"
); }); app.Run(async
context => {await
context.Response.WriteAsync("end of pipeline.\r\n"
); }); /<code>
這三個中間件配置到管道後,輸出的結果與管道模型的圖示是一致的:
<code>middleware1
begin
middleware2
begin
end
of pipeline.
middleware2
end
middleware1
end
/<code>
可以看到除了最後一箇中間件,前面的中間件都調用了await next.Invoke(),next參數表示管道中的下一個委託,如果不調用 next,後面的中間件就不知執行,這稱為管道的短路。通常中間件都應該自覺調用下一個中間件,但有的中間件會故意造成短路,比如授權中間件、靜態文件中間件等。
Run
Run的委託中沒有next參數,這就意味著它會稱為最後一箇中間件(終端中間件),此外可以故意短路的Use委託也可能會成為終端中間件。
Map,提供了創建管道分支的能力
Map擴展用作約定來創建管道分支,會基於給定請求路徑的匹配項來創建請求管道分支,如果請求路徑以給定路徑開頭,則執行分支。
<code>
private
static
void
HandleMapTest1
(IApplicationBuilder app
) { app.Run(async
context => {await
context.Response.WriteAsync("Map Test 1"
); }); }private
static
void
HandleMapTest2
(IApplicationBuilder app
) { app.Run(async
context => {await
context.Response.WriteAsync("Map Test 2"
); }); }public
void
Configure
(IApplicationBuilder app
) { app.Map("/map1"
, HandleMapTest1); app.Map("/map2"
, HandleMapTest2); app.Run(async
context => {await
context.Response.WriteAsync("Hello from non-Map delegate.
/<code>
這段代碼為管道創建了三個分支:
URLResponselocalhost:1234Hello from non-Map delegate.localhost:1234/map1Map Test 1localhost:1234/map2Map Test 2
Map還支持嵌套
<code>app.Map("/level1"
,level1App
=> { level1App.Map("/level2a"
,level2AApp
=> { }); level1App.Map("/level2b"
,level2BApp
=> { }); }); /<code>
MapWhen,滿足條件時創建分支
<code>app.MapWhen(context
=> context.Request.Query.ContainsKey("branch"
),branch
=> { }); /<code>
此外,UseWhen也可以根據條件創建分支,它與MapWhen的區別在於,使用UseWhen創建的分支如果不包含終端中間件,則會重新匯入主管道。
自定義中間件
通常使用內置的中間件可滿足大多數場合,但如果有需要也可以創建自定義中間件。假設有這樣一個嵌入式中間件,可以通過查詢字符串設置當前請求的區域,比如https://localhost:5000/?culture=zh-cn,會將CurrentCulture設置為Chinese Simplified。現在要將其封裝為可重用的獨立的中間件。
<code>public
void
Configure
(IApplicationBuilder app
) { app.Use(async
(context, next) => {var
cultureQuery = context.Request.Query["culture"
];if
(!string
.IsNullOrWhiteSpace(cultureQuery)) {var
culture =new
CultureInfo(cultureQuery); CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; }await
next(); }); app.Run(async
(context) => {await
context.Response.WriteAsync($"Hello
{CultureInfo.CurrentCulture.DisplayName}
"); }); } /<code>
在開始封裝前,先了解一下對中間件的要求:
- 中間件必須具有類型為RequestDelegate的參數的公共構造函數,之前代碼中看到next.Invoke(),其中next就是下一個中間件的RequestDelegate;
- 名為 Invoke 或 InvokeAsync 的公共方法。而且這個方法的返回類型為Task,且第一個參數的必須是HttpContext,其它的參數由DI容器解析。
基於上述要求,編寫的中間件為:
<code>public
class
RequestCultureMiddleware
{private
readonly
RequestDelegate _next;public
RequestCultureMiddleware
(RequestDelegate next
) { _next = next; }public
async
TaskInvokeAsync
(HttpContext context
) {var
cultureQuery = context.Request.Query["culture"
];if
(!string
.IsNullOrWhiteSpace(cultureQuery)) {var
culture =new
CultureInfo(cultureQuery); CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; }await
_next(context); } } /<code>
然後就可以使用了:
<code>app
.UseMiddleware
<RequestCultureMiddleware
>(); /<code>
還可以進一步封裝為IApplicationBuilder的擴展方法:
<code>public
static
class
RequestCultureMiddlewareExtensions
{public
static
IApplicationBuilderUseRequestCulture
(
this
IApplicationBuilder builder) {return
builder.UseMiddleware(); } } /<code>
然後就可以像內置的中間件一樣了:
<code>app
.UseRequestCulture
();/<code>
原文地址:https://www.cnblogs.com/zhixin9001/p/12778964.html