一、什么是中間件?先看看微軟官方文檔對中間件的定義:
官網地址:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0
官網還給出了一張中間件調用的示意圖,如下:
簡而言之,我們可以把中間件理解成一個功能,它可以對請求進行處理,
並決定是將處理過的請求傳遞給下一個中間件繼續處理還是結束傳遞,直接發送一個響應給客戶端。
二、如果要自定義一個中間件,在請求到達自定義的中間件之前會不會有其他中間件進行處理?
如果有,是哪些中間件,其順序是怎么樣的?
使用VS2019新建一個WebApi項目后,系統會默認添加一些中間件,比如 app.UseRouting( ); 、
app.UseAuthorization( ); 、app.UseEndpoints( )等(一般是app.UseXxxxx( )這樣的形式),
它們位於 Startup.cs 的 Configure(IApplicationBuilder app, IWebHostEnvironment env) 方法中,
其執行順序即是添加的順序。對於這些默認的中間件我們暫時不去深究,大概的執行流程
可以參考官方文檔中ASP.NET Core MVC 和 Razor Pages 應用的完整請求處理管道示意圖,如下:
三、實現一個自定義的中間件。
功能:客戶端請求應用程序的一張圖片,自定義中間件根據客戶端身份信息決定是返回此圖片還是返回一張 "沒有授權" 的圖片。
使用場景:WebApi 給不同的客戶端提供服務,不同的客戶端有自己的圖片文件夾,比如 A公司對應的圖片文件夾是 A_Images,
B公司對應的圖片文件夾是B_Images,本公司內部管理員對應的圖片文件夾是Admin_Images等,
此功能是防止A公司發送的URL錯寫成B公司的從而造成B公司的圖片信息泄露。
擴展:此功能應用到網站中可以稍加修改設計成圖片防盜鏈的功能。
客戶端請求的圖片如下(lion.jpg):
如果是沒授權的客戶端,響應如下的一張圖片(hellokitty.png):
四、實現步驟。
第1步,預備工作:
新建一個新的 .NET CORE WEBAPI 項目名叫 webapidemo3,
按慣例先刪除項目默認生成的 WeatherForecast.cs 和 WeatherForecastController.cs,
在Controllers文件夾下新增一個ImagesController的控制器,在此控制器中新增終結點 Demo1( ),代碼如下:
namespace webapidemo3 { [Route("api/[controller]")] [ApiController] public class ImagesController : ControllerBase { [HttpGet] [Route("demo1")] public IActionResult Demo1() { return Ok("Test..."); } } }
第2步,在VS2019解決方案中新建一個 Middlewares 的文件夾,
在里面新增一個名為 ImageRequestMiddleware.cs 的中間件類,如下:
新增 ImageRequestMiddleware.cs 后的默認代碼如下:
namespace webapidemo3 { // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project public class ImageRequestMiddleware { private readonly RequestDelegate _next; public ImageRequestMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext httpContext) { return _next(httpContext); } } // Extension method used to add the middleware to the HTTP request pipeline. public static class ImageRequestMiddlewareExtensions { public static IApplicationBuilder UseImageRequestMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<ImageRequestMiddleware>(); } } }
要完成的功能需要在 "public Task Invoke(HttpContext httpContext)" 這個函數中進行編碼。
類 "public static class ImageRequestMiddlewareExtensions" 的作用是
提供一個擴展方法將這個中間件注冊到 HTTP 請求管道中,具體就是在 Startup.cs 的
Configure(IApplicationBuilder app, IWebHostEnvironment env) 方法中加入如下一行代碼(見紅色部分):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseImageRequestMiddleware(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
注意:中間件的執行是有順序的,我們自定義的中間件放在客戶端經過認證后(app.UseAuthorization( );),
終結點執行前(app.UseEndpoints( )),實際項目中根據需要更改位置。
第3步,編碼實現,代碼如下。
public class ImageRequestMiddleware { private readonly RequestDelegate _next; private readonly string _wwwrootPath; public ImageRequestMiddleware(RequestDelegate next, IWebHostEnvironment env) { _next = next; //用構造函數注入的方式獲取應用程序WebRoot目錄的物理地址,即wwwroot文件夾的路徑(包含wwwroot) _wwwrootPath = env.WebRootPath; } public Task Invoke(HttpContext httpContext) { //因為此中間件是在認證中間件之后執行的, //所以companyId應該從客戶端傳遞的認證信息中解析出來 //具體的獲取方式和項目的身份認證方式相關,以什么方式寫入就以什么方式取出 //如果是cookie認證,可能的取值方式如下 //string companyId = httpContext.Request.Cookies["companyId"]; string companyId = "A1234"; //假設從客戶端解析出來的companyId=A1234 string authorizedImgUrl = "img/" + companyId; //客戶端有權限訪問的圖片路徑 string url = httpContext.Request.Path;//客戶端訪問的網址, //只處理圖片,如果訪問網址不包含圖片則直接進入下一個中間件的處理 //這里根據實際情況可以加入.png/.jpeg/.gif等圖片格式的判斷 if (!url.EndsWith(".jpg", true, CultureInfo.CurrentCulture)) { return _next(httpContext); } string imgPath; if (url.IndexOf(authorizedImgUrl) > 0) //url中圖片路徑和授權訪問的圖片路徑一致 { imgPath = url.Substring(url.IndexOf(authorizedImgUrl)); } else //url中圖片路徑和授權訪問的圖片路徑不一致就響應指定的圖片 { imgPath = "img/hellokitty.png"; } //輸出圖片文件 httpContext.Response.SendFileAsync(Path.Combine(_wwwrootPath, imgPath)); //中間件短路,不繼續執行下一個中間件 return null; } }
注:中間件中如果有Response返回后,不會再向下傳遞了(即調用 Next( ) 無效)。
五、測試。
准備工作:
1. 在項目根目錄下新增 wwwroot 的文件夾,然后再新增如下的文件夾和文件:
注意:wwwroot這個文件夾名稱有特殊的含義,這里通常存放image、js、css等資源,
在網址中訪問其中的圖片的時候是看不到 wwwroot 這個路徑的。
2. 在 Startup.cs 的 Configure( ) 方法中注冊自定義的中間件,如下(見紅色部分代碼),
注意添加的位置,實際項目中如果順序不恰當可能達不到我們要的效果:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseImageRequestMiddleware(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
3. 打開瀏覽器,我們先訪問終結點 Demo1() 看看會不會錯誤的把圖片顯示出來。
是我們期望的結果。
4. 再訪問網址: http://localhost:51630/img/A1234/lion.jpg ,結果如下:
有 "img/A1234" 這個路徑的權限,可以正常得到圖片內容。
5. 最后訪問網址: http://localhost:51630/img/B5678/lion.jpg ,結果如下:
沒有 "img/B5678" 這個路徑的權限,輸出指定的圖片,達到效果。