## 1.Asp.Net Core Middleware(中間件)
1.1 中間件
(1)什么是中間件
- 中間件是ASP.NET Core的核心組件,MVC框架、響應緩存、身份驗證、CORS、Swagger等都是內置中間件。
-
廣義上來講:Tomcat、WebLogic、Redis、IIS;狹義上來講,ASP.NET Core中的中間件指ASP.NET Core中的一個組件。
-
中間件由前邏輯、next、后邏輯3部分組成,前邏輯為第一段要執行的邏輯代碼、next為指向下一個中間件的調用、后邏輯為從下一個中間件執行返回所執行的邏輯代碼。每個HTTP請求都要經歷一系列中間件的處理,每個中間件對於請求進行特定的處理后,再轉到下一個中間件,最終的業務邏輯代碼執行完成后,響應的內容也會按照處理的相反順序進行處理,然后形成HTTP響應報文返回給客戶端。
-
中間件組成一個管道,整個ASP.NET Core的執行過程就是HTTP請求和響應按照中間件組裝的順序在中間件之間流轉的過程。開發人員可以對組成管道的中間件按照需要進行自由組合
(2)中間件的三個概念
Map、Use和Run。Map用來定義一個管道可以處理哪些請求,Use和Run用來定義管道,一個管道由若干個Use和一個Run組成,每個Use引入一個中間件,而Run是用來執行最終的核心應用邏輯。
(3)理解中間件執行順序
app.Map("/test", async appbuilder => {
appbuilder.Use(async (context, next) => {
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("1 Start<br/>");
await next.Invoke();
await context.Response.WriteAsync("1 End<br/>");
});
appbuilder.Use(async (context, next) => {
await context.Response.WriteAsync("2 Start<br/>");
await next.Invoke();
await context.Response.WriteAsync("2 End<br/>");
});
appbuilder.Run(async ctx => {
await ctx.Response.WriteAsync("hello middleware <br/>");
});
});
1.2 中間件的基本實現
中間件類是一個普通的.NET類,它不需要繼承任何父類或者實現任何接口,但是這個類需要有一個構造方法,構造方法至少要有一個RequestDelegate類型的參數,這個參數用來指向下一個中間件。這個類還需要定義一個名字為Invoke或InvokeAsync的方法,方法至少有一個HttpContext類型的參數,方法的返回值必須是Task類型。中間件類的構造方法和Invoke(或InvokeAsync)方法還可以定義其他參數,其他參數的值會通過依賴注入自動賦值。
1.3 Markdown轉html中間件
(1)安裝Ude.NetStandard,獲取流的編碼
Install-Package Ude.NetStandard
(2)安裝MarkdownSharp,md文件轉html
Install-Package MarkdownSharp
(3)MarkDownViewerMiddleware
public class MarkDownViewerMiddleware
{
private readonly RequestDelegate next;
private readonly IWebHostEnvironment hostEnv;
private readonly IMemoryCache memCache;
public MarkDownViewerMiddleware(RequestDelegate next,
IWebHostEnvironment hostEnv, IMemoryCache memCache)
{
this.next = next;
this.hostEnv = hostEnv;
this.memCache = memCache;
}
/// <summary>
/// 檢測流的編碼
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
private static string DetectCharset(Stream stream)
{
CharsetDetector charDetector = new();
charDetector.Feed(stream);
charDetector.DataEnd();
string charset = charDetector.Charset ?? "UTF-8";
stream.Position = 0;
return charset;
}
/// <summary>
/// 讀取文本內容
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
private static async Task<string> ReadText(Stream stream)
{
string charset = DetectCharset(stream);
using var reader = new StreamReader(stream, Encoding.GetEncoding(charset));
return await reader.ReadToEndAsync();
}
public async Task InvokeAsync(HttpContext context)
{
string path = context.Request.Path.Value ?? "";
if (!path.EndsWith(".md"))
{
await next(context);
return;
}
var file = hostEnv.WebRootFileProvider.GetFileInfo(path);
if (!file.Exists)
{
await next(context);
return;
}
context.Response.ContentType = $"text/html;charset=UTF-8";
context.Response.StatusCode = 200;
string cacheKey = nameof(MarkDownViewerMiddleware)
+ path + file.LastModified;
var html = await memCache.GetOrCreateAsync(cacheKey, async ce =>
{
ce.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
using var stream = file.CreateReadStream();
string text = await ReadText(stream);
Markdown markdown = new Markdown();
return markdown.Transform(text);
});
await context.Response.WriteAsync(html);
}
}
(4)app.UseMiddleware
MarkDownViewerMiddleware操作的是靜態文件,所以要在UseStaticFiles之前,否則會被當做靜態文件直接輸出
1.4 Filter和Middware的區別
-
中間件是ASP.NET Core這個基礎提供的功能,而Filter是ASP.NET Core MVC中提供的功能。ASP.NET Core MVC是由MVC中間件提供的框架,而Filter屬於MVC中間件提供的功能。
-
中間件可以處理所有的請求,而Filter只能處理對控制器的請求;中間件運行在一個更底層、更抽象的級別,因此在中間件中無法處理MVC中間件特有的概念。
-
中間件和Filter可以完成很多相似的功能。“未處理異常中間件”和“未處理異常Filter”;“請求限流中間件”和“請求限流Filter”的區別。
-
優先選擇使用中間件;但是如果這個組件只針對MVC或者需要調用一些MVC相關的類的時候,我們就只能選擇Filter。