說到ASP.NET CORE 管道模型不得不先來看看之前的ASP.NET 的管道模型,兩者差異很大,.NET CORE 3.1 后完全重新設計了框架的底層,.net core 3.1 的管道模型更加靈活便捷,可做到熱插拔,通過管道可以隨意注冊自己想要的服務或者第三方服務插件.
ASP.NET 管道
請求進入ASP.NET 作業行程后,由行程創建HttpWorkRequest 物件,封裝此次請求有關的所有資訊,然后進入HttpRuntime 類進行進一步的處理,HttpRuntime 通過請求資訊創建HttpContext 背景關系物件,此物件將貫穿整個管道,直到回應結束,同時創建或從應用程式池里初始化一個HttpApplication物件,由此物件開始處理之前注冊的多個HttpModule,之后呼叫HandlerFactory 創建Handler處理程式,最終處理此次請求內容,生存回應回傳,
以前的管道模型是全家桶方式,所有的管道不支持熱插拔,一次性全部集成在里面,所有這也是ASP.NET 沒有.NET CORE 性能好的一大原因所在,
IHttpModule 和IHttpHandler 已經不復存在了,取而代之的是一個個中間件(Middleware),Server將接收到的請求直接向后傳遞,依次經過每一個中間件進行處理,然后由最后一個中間件處理并生成回應內容后回傳,再反向以此經過每個中間件,直到由Server發送出去,中間件就像一層一層的“濾網”,過濾所有的請求和回應,這一設計非常適用于“請求-回應”這樣的場景--訊息從管道頭流入最后反向流出,
ASP.NET Core是一套全新的平臺,已經不再向前兼容,設計更追求組件化,追求高性能,沒有全家桶,那么ASP.NET Core是怎么搭建請求管道的呢?默認情況,管道只有一個404,然后你也可以增加請求的處理,這就是以前的Handler,只包含業務處理環節,其他的就是中間件,MiddleWare,
我們現在來看下幾種中間件注冊的模式:
以下的代碼都把Configure 中的代碼全部注釋的情況下從零代碼開始一個一個注冊演示
- 終結者模式
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Console.WriteLine("Configure");
app.Run(async (HttpContext context) => {
await context.Response.WriteAsync("Hello World Run");
});
app.Run(async (HttpContext context) => {
await context.Response.WriteAsync("Hello World Run Again");
});
}
運行代碼后瀏覽器可以看到結果如下:
從上面的運行結果可以看出 Run 終結式 只是執行,沒有去呼叫Next ,一般作為終結點,所謂Run終結式注冊,其實只是一個擴展方法,最侄訓不是得呼叫Use方法,
- Use 方式注冊
代碼如下:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World Use1 <br/>");
await next();//呼叫下一個中間件
await context.Response.WriteAsync("Hello World Use1 End <br/>");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World Use2 Again <br/>");
await next();
});
}
以上代碼得出的結果如下:
Hello World Use1
Hello World Use2 Again
從運行結果 中hello world use 1 end
并未執行,主要是在它上面 next() 呼叫了下一個中間件,到那里已經終結到下一個中間件執行去了,
再來看下面的代碼運行結果:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World Use1 <br/>");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World Use2 <br/>");
});
}
結果如圖:
第二個中間件也并未得到執行,use 方式注冊中間件得出的結論是:Use注冊動作 不是終結點 ,執行next,就可以執行下一個中間件 如果不執行,就等于Run
- UseWhen可以對HttpContext檢測后,增加處理環節;原來的流程還是正常執行的,代碼如下 該方式注冊可以實作一系列的驗證攔截等操作,從管道的上一層管道進行合理性攔截匹配等等系列過濾,可以說類似于Filter 的實作
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseWhen(context =>
{
return context.Request.Query.ContainsKey("Name");
},
appBuilder =>
{
appBuilder.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World Use3 Again Again Again <br/>");
await next();
});
});
}
看了上面的幾個管道應用模塊的注冊,我們再來一起解讀下源代碼
IApplicationBuilder 應用程式的組裝者,RequestDelegate:傳遞一個HttpContext,異步操作下,不回傳;也就是一個處理動作,Use(Func<RequestDelegate, RequestDelegate> middleware) 委托,傳入一個RequestDelegate,回傳一個RequestDelegate,ApplicationBuilder里面有個容器IList<Func<RequestDelegate, RequestDelegate>> _components,Use就只是去容器里面添加個元素,最侄訓Build()一下, 如果沒有任何注冊,就直接404處理,
核心代碼如下:
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
public RequestDelegate Build()
{
RequestDelegate app = context =>
{
// If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
// This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
var endpoint = context.GetEndpoint();
var endpointRequestDelegate = endpoint?.RequestDelegate;
if (endpointRequestDelegate != null)
{
var message =
$"The request reached the end of the pipeline without executing the endpoint: '{endpoint.DisplayName}'. " +
$"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
$"routing.";
throw new InvalidOperationException(message);
}
context.Response.StatusCode = 404;
return Task.CompletedTask;
};
foreach (var component in _components.Reverse())
{
app = component(app);
}
return app;
}
IApplicationBuilder build之后其實就是一個RequestDelegate,能對HttpContext加以處理,默認情況下,管道是空的,就是404;可以根據你的訴求,任意的配置執行,一切全部由開發者自由定制,框架只是提供了一個組裝方式
看了源代碼后我們再來對上面的中間件進行優雅的封裝,封裝后的代碼如下:
public class FirstMiddleWare
{
private readonly RequestDelegate _next;
public FirstMiddleWare(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync($"{nameof(FirstMiddleWare)},Hello World1!<br/>");
await _next(context);
await context.Response.WriteAsync($"{nameof(FirstMiddleWare)},Hello World2!<br/>");
}
}
使用注冊中間件
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<FirstMiddleWare>();
}
我們可以再升級一點點,使用擴展方法,將這個類中的邏輯作為IApplicationBuilder的擴展方法,
public static class MiddleExtend
{
public static IApplicationBuilder UseFirstMiddleWare(this IApplicationBuilder builder)
{
return builder.UseMiddleware<FirstMiddleWare>();
}
}
使用時代碼如下:
app.UseFirstMiddleWare();
到這里.net core 管道模型和中間件注冊使用已經告一段落了,后續我們繼續來分享.net core 中的過濾器使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/67538.html
標籤:其他
