HttpPipeline是ASP.NET Core中重要的一個概念,這個概念在開發中經常會用到。它通過定義一系列的middleware來組織請求的處理流程,可以方便地對請求進行處理。本文將從不同的方面,探討HttpPipeline如何工作。
一、Pipeline的組成
HTTP請求在ASP.NET Core中經過一個由若干個中間件組成的管道,這個管道被稱之為Pipeline。ASP.NET Core應用程序的Startup類就是用來定義和組織Pipeline的。Startup類中有兩個方法:ConfigureServices和Configure方法。
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 配置依賴注入
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 配置HTTP請求管道
}
}
其中ConfigureServices方法是用來配置依賴注入,Configure方法是用來配置HTTP請求管道。在Configure方法中,可以向管道中添加中間件,Middleware的順序將決定它們在管道中的執行順序。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}");
});
}
上述代碼中,我們向管道中添加了以下五個Middleware:
- UseHttpsRedirection : HTTP請求重定向到HTTPS
- UseStaticFiles : 處理靜態文件請求
- UseRouting : 處理請求路徑
- UseAuthorization : 處理授權
- UseEndpoints : 處理路由匹配
基本的請求處理流程如下圖所示:
二、Middleware的工作原理
Middleware是處理請求的核心部件,也是Pipeline的基本單位。Middleware負責處理請求並調用管道中下一個Middleware。下一個Middleware可以通過HttpContext.RequestDelegate指定,比如:
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 處理請求前的邏輯
await _next(context);
// 處理請求後的邏輯
}
}
上述代碼中,MyMiddleware是一個簡單的Middleware,它在處理請求前和請求後都會有相應的邏輯。在MyMiddleware的構造函數中,我們注入了HttpContext.RequestDelegate類型的參數_next,它表示了Pipeline中下一個Middleware。在MyMiddleware的InvokeAsync方法中,我們首先執行自己的邏輯,然後調用_next委託,以便讓執行流程繼續下去。
如果你需要在管道中使用自己的Middleware,首先需要定義這個Middleware,在Startup類的Configure方法中引入自己定義的Middleware:
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<MyMiddleware>();
// 省略其他Middleware的配置
}
這樣,自己定義的Middleware就被引入到了Pipeline中。
三、Middleware的生命周期
ASP.NET Core中定義了三種Middleware的生命周期,分別是Transistent、Scoped、Singleton。
- Transient: 瞬時,實例每次請求都會創建
- Scoped: 域,每次請求都只會創建一個實例
- Singleton: 單例,實例在應用程序生命周期內只會被創建一次
為了了解生命周期的區別,我們可以通過一個簡單的示例來說明:
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
public MyService()
{
Console.WriteLine("MyService實例被創建");
}
public void DoSomething()
{
Console.WriteLine("MyService正在處理事務");
}
}
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly IMyService _service;
public MyMiddleware(RequestDelegate next, IMyService service)
{
_next = next;
_service = service;
}
public async Task InvokeAsync(HttpContext context)
{
_service.DoSomething();
await _next(context);
}
}
上述代碼中,我們定義了一個IMyService接口和MyService實現類,還有一個MyMiddleware,它需要注入IMyService類型的service。在MyMiddleware的InvokeAsync方法中,我們首先調用service.DoSomething()方法來對IMyService實例進行操作。
在Startup類的ConfigureServices方法中,我們將IMyService註冊到依賴注入容器中,使用Transient生命周期:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
}
這意味着每次請求都會創建一個新的IMyService實例。當我們使用這個Pipeline時,會看到多次創建MyService的實例:
MyService實例被創建
MyService實例被創建
然後我們將IMyService的生命周期從Transient改成Scoped:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMyService, MyService>();
}
這意味着,每次請求將使用同一個IMyService實例。輸出結果為:
MyService實例被創建
最後我們將生命周期改成Singleton:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMyService, MyService>();
}
這將保證整個應用程序生命周期內只會創建一個IMyService實例,輸出結果為:
MyService實例被創建
四、Middleware執行順序
在管道中,Middleware的順序對於請求的處理流程至關重要。每個Middleware只負責它自己的業務邏輯,所以每個Middleware是互相獨立的。每個Middleware的順序會對整個Pipeline的處理造成影響。
Middleware的順序可以通過在Configure方法中的調用順序決定:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 20210517 加入自定義的 Middleware
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
除此之外,Middleware還可以通過條件控制順序。例如:
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/admin"))
{
await context.Response.WriteAsync("你需要管理員權限才能訪問管理頁面");
}
else
{
await next();
}
});
// 省略其他Middleware的配置
}
在上述代碼中,我們使用了一個匿名Middleware,它檢查請求路徑是否以”/admin”開頭。如果請求路徑以”/admin”開頭,將返回一條錯誤信息。這個Middleware是在管道的開始處位置,這意味着即使後面的Middleware能夠處理任何類型的請求,如果請求路徑以”/admin”開頭,這個Middleware也會優先執行。
五、自定義Middleware
在使用ASP.NET Core時,我們可能需要自己編寫一些Middleware來處理請求。編寫Middleware非常簡單,只需要實現Invoke方法即可:
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// 處理請求前的邏輯
await _next(context);
// 處理請求後的邏輯
}
}
在ASP.NET Core中,Middleware通常是由一個Invoke方法和一個構造函數組成的。Invoke方法是用來處理請求的,構造函數是用來接收參數的。我們可以在Invoke方法中執行任何邏輯。例如,我們可以修改請求或者響應的內容,或者執行任何附加邏輯(如日誌記錄)。
在編寫Middleware時,我們需要記住以下幾點:
- 每個Middleware必須使用下一個Middleware,否則整個Pipeline的處理過程將被打斷。
- 每個Middleware應該儘可能做到高內聚、低耦合。
- Middleware的順序對請求的處理流程至關重要,必須慎重選擇。
六、結語
ASP.NET Core中的HttpPipeline有着重要的作用,通過定義一系列的middleware來組織請求的處理流程,方便地對請求進行處理。在實際開發中,我們會經常使用到Middleware,因此理解Pipeline的工作原理是非常重要的。希望本文能夠對你有所幫助。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/199122.html