91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ASP.NET Core中間件Middleware怎么用

發布時間:2021-09-02 13:44:08 來源:億速云 閱讀:230 作者:小新 欄目:開發技術

這篇文章將為大家詳細講解有關ASP.NET Core中間件Middleware怎么用,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

中間件

先借用微軟官方文檔的一張圖:

ASP.NET Core中間件Middleware怎么用

可以看到,中間件實際上是一種配置在HTTP請求管道中,用來處理請求和響應的組件。它可以:

  • 決定是否將請求傳遞到管道中的下一個中間件

  • 可以在管道中的下一個中間件處理之前和之后進行操作

此外,中間件的注冊是有順序的,書寫代碼時一定要注意!

中間件管道

Run

該方法為HTTP請求管道添加一個中間件,并標識該中間件為管道終點,稱為終端中間件。也就是說,該中間件就是管道的末尾,在該中間件之后注冊的中間件將永遠都不會被執行。所以,該方法一般只會書寫在Configure方法末尾。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

Use

通過該方法快捷的注冊一個匿名的中間件

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // 下一個中間件處理之前的操作
            Console.WriteLine("Use Begin");
            
            await next();
            
            // 下一個中間件處理完成后的操作
            Console.WriteLine("Use End");
        });
    }
}

注意:

  • 1.如果要將請求發送到管道中的下一個中間件,一定要記得調用next.Invoke / next(),否則會導致管道短路,后續的中間件將不會被執行

  • 2.在中間件中,如果已經開始給客戶端發送Response,請千萬不要調用next.Invoke / next(),也不要對Response進行任何更改,否則,將拋出異常。

  • 3.可以通過context.Response.HasStarted來判斷響應是否已開始。

以下都是錯誤的代碼寫法

錯誤1:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            await context.Response.WriteAsync("Use");
            await next();
        });

        app.Run(context =>
        {
            // 由于上方的中間件已經開始 Response,此處更改 Response Header 會拋出異常
            context.Response.Headers.Add("test", "test");
            return Task.CompletedTask;
        });
    }
}

錯誤2

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            await context.Response.WriteAsync("Use");
            
            // 即使沒有調用 next.Invoke / next(),也不能在 Response 開始后對 Response 進行更改
            context.Response.Headers.Add("test", "test");
        });
    }
}

UseWhen

通過該方法針對不同的邏輯條件創建管道分支。需要注意的是:

進入了管道分支后,如果管道分支不存在管道短路或終端中間件,則會再次返回到主管道。

當使用PathString時,路徑必須以“/”開頭,且允許只有一個'/'字符

支持嵌套,即UseWhen中嵌套UseWhen等

支持同時匹配多個段,如 /get/user

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // /get 或 /get/xxx 都會進入該管道分支
        app.UseWhen(context => context.Request.Path.StartsWithSegments("/get"), app =>
        {
            app.Use(async (context, next) =>
            {
                Console.WriteLine("UseWhen:Use");

                await next();
            });
        });
        
        app.Use(async (context, next) =>
        {
            Console.WriteLine("Use");

            await next();
        });

        app.Run(async context =>
        {
            Console.WriteLine("Run");

            await context.Response.WriteAsync("Hello World!");
        });
    }
}

當訪問 /get 時,輸出如下:

UseWhen:Use
Use
Run

如果你發現輸出了兩遍,別慌,看看是不是瀏覽器發送了兩次請求,分別是 /get 和 /favicon.ico

Map

  • 通過該方法針對不同的請求路徑創建管道分支。需要注意的是:

  • 一旦進入了管道分支,則不會再回到主管道。

  • 使用該方法時,會將匹配的路徑從HttpRequest.Path 中刪除,并將其追加到HttpRequest.PathBase中。

  • 路徑必須以“/”開頭,且不能只有一個'/'字符

  • 支持嵌套,即Map中嵌套Map、MapWhen(接下來會講)等

  • 支持同時匹配多個段,如 /post/user

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // 訪問 /get 時會進入該管道分支
        // 訪問 /get/xxx 時會進入該管道分支
        app.Map("/get", app =>
        {
            app.Use(async (context, next) =>
            {
                Console.WriteLine("Map get: Use");
                Console.WriteLine($"Request Path: {context.Request.Path}"); 
                Console.WriteLine($"Request PathBase: {context.Request.PathBase}");
        
                await next();
            });
        
            app.Run(async context =>
            {
                Console.WriteLine("Map get: Run");
        
                await context.Response.WriteAsync("Hello World!");
            });
        
        });
        
        // 訪問 /post/user 時會進入該管道分支
        // 訪問 /post/user/xxx 時會進入該管道分支
        app.Map("/post/user", app =>
        {
            // 訪問 /post/user/student 時會進入該管道分支
            // 訪問 /post/user/student/1 時會進入該管道分支
            app.Map("/student", app =>
            {
                app.Run(async context =>
                {
                    Console.WriteLine("Map /post/user/student: Run");
                    Console.WriteLine($"Request Path: {context.Request.Path}");
                    Console.WriteLine($"Request PathBase: {context.Request.PathBase}");
        
                    await context.Response.WriteAsync("Hello World!");
                });
            });
            
            app.Use(async (context, next) =>
            {
                Console.WriteLine("Map post/user: Use");
                Console.WriteLine($"Request Path: {context.Request.Path}");
                Console.WriteLine($"Request PathBase: {context.Request.PathBase}");
                
                await next();
            });
        
            app.Run(async context =>
            {
                Console.WriteLine("Map post/user: Run");
        
                await context.Response.WriteAsync("Hello World!");
            });
        });
    }
}

當你訪問 /get/user 時,輸出如下:

Map get: Use
Request Path: /user
Request PathBase: /get
Map get: Run

當你訪問 /post/user/student/1 時,輸出如下:

Map /post/user/student: Run
Request Path: /1
Request PathBase: /post/user/student

其他情況交給你自己去嘗試啦!

MapWhen

Map類似,只不過MapWhen不是基于路徑,而是基于邏輯條件創建管道分支。注意事項如下:

  • 一旦進入了管道分支,則不會再回到主管道。

  • 當使用PathString時,路徑必須以“/”開頭,且允許只有一個'/'字符

  • HttpRequest.PathHttpRequest.PathBase不會像Map那樣進行特別處理

  • 支持嵌套,即MapWhen中嵌套MapWhen、Map等

  • 支持同時匹配多個段,如 /get/user

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // /get 或 /get/xxx 都會進入該管道分支
        app.MapWhen(context => context.Request.Path.StartsWithSegments("/get"), app =>
        {
            app.MapWhen(context => context.Request.Path.ToString().Contains("user"), app =>
            {
                app.Use(async (context, next) =>
                {
                    Console.WriteLine("MapWhen get user: Use");

                    await next();
                });
            });
        
            app.Use(async (context, next) =>
            {
                Console.WriteLine("MapWhen get: Use");
        
                await next();
            });
        
            app.Run(async context =>
            {
                Console.WriteLine("MapWhen get: Run");
        
                await context.Response.WriteAsync("Hello World!");
            });
        });
    }
}

當你訪問 /get/user 時,輸出如下:

MapWhen get user: Use

可以看到,即使該管道分支沒有終端中間件,也不會回到主管道。

Run & Use & UseWhen & Map & Map

一下子接觸了4個命名相似的、與中間件管道有關的API,不知道你有沒有暈倒,沒關系,我來幫大家總結一下:

  • Run用于注冊終端中間件,Use用來注冊匿名中間件,UseWhenMapMapWhen用于創建管道分支。

  • UseWhen進入管道分支后,如果管道分支中不存在短路或終端中間件,則會返回到主管道。MapMapWhen進入管道分支后,無論如何,都不會再返回到主管道。

  • UseWhenMapWhen基于邏輯條件來創建管道分支,而Map基于請求路徑來創建管道分支,且會對HttpRequest.PathHttpRequest.PathBase進行處理。

編寫中間件并激活

上面已經提到過的RunUse就不再贅述了。

基于約定的中間件

“約定大于配置”,先來個約法三章:

  • 1.擁有公共(public)構造函數,且該構造函數至少包含一個類型為RequestDelegate的參數

  • 2.擁有名為InvokeInvokeAsync的公共(public)方法,必須包含一個類型為HttpContext的方法參數,且該參數必須位于第一個參數的位置,另外該方法必須返回Task類型。

  • 3.構造函數中的其他參數可以通過依賴注入(DI)填充,也可以通過UseMiddleware傳參進行填充。

通過DI填充時,只能接收 Transient 和 Singleton 的DI參數。這是由于中間件是在應用啟動時構造的(而不是按請求構造),所以當出現 Scoped 參數時,構造函數內的DI參數生命周期與其他不共享,如果想要共享,則必須將Scoped DI參數添加到Invoke/InvokeAsync來進行使用。

通過UseMiddleware傳參時,構造函數內的DI參數和非DI參數順序沒有要求,傳入UseMiddleware內的參數順序也沒有要求,但是我建議將非DI參數放到前面,DI參數放到后面。(這一塊感覺微軟做的好牛皮)

  • 4.Invoke/InvokeAsync的其他參數也能夠通過依賴注入(DI)填充,可以接收 Transient、Scoped 和 Singleton 的DI參數。

一個簡單的中間件如下:

public class MyMiddleware
{
    // 用于調用管道中的下一個中間件
    private readonly RequestDelegate _next;

    public MyMiddleware(
        RequestDelegate next,
        ITransientService transientService,
        ISingletonService singletonService)
    {
        _next = next;
    }

    public async Task InvokeAsync(
        HttpContext context,
        ITransientService transientService,
        IScopedService scopedService,
        ISingletonService singletonService)
    {
        // 下一個中間件處理之前的操作
        Console.WriteLine("MyMiddleware Begin");
        
        await _next(context);
        
        // 下一個中間件處理完成后的操作
        Console.WriteLine("MyMiddleware End");
    }
}

然后,你可以通過UseMiddleware方法將其添加到管道中

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<MyMiddleware>();
    }
}

不過,一般不推薦直接使用UseMiddleware,而是將其封裝到擴展方法中

public static class AppMiddlewareApplicationBuilderExtensions
{
    public static IApplicationBuilder UseMy(this IApplicationBuilder app) => app.UseMiddleware<MyMiddleware>();
}

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMy();
    }
}

基于工廠的中間件

優勢:

  • 按照請求進行激活。這個就是說,上面基于約定的中間件實例是單例的,但是基于工廠的中間件,可以在依賴注入時設置中間件實例的生命周期。

  • 使中間件強類型化(因為其實現了接口IMiddleware

該方式的實現基于IMiddlewareFactoryIMiddleware。先來看一下接口定義:

public interface IMiddlewareFactory
{
    IMiddleware? Create(Type middlewareType);

    void Release(IMiddleware middleware);
}

public interface IMiddleware
{
    Task InvokeAsync(HttpContext context, RequestDelegate next);
}

你有沒有想過當我們調用UseMiddleware時,它是如何工作的呢?事實上,UseMiddleware擴展方法會先檢查中間件是否實現了IMiddleware接口。 如果實現了,則使用容器中注冊的IMiddlewareFactory實例來解析該IMiddleware的實例(這下你知道為什么稱為“基于工廠的中間件”了吧)。如果沒實現,那么就使用基于約定的中間件邏輯來激活中間件。

注意,基于工廠的中間件,在應用的服務容器中一般注冊為 Scoped 或 Transient 服務

這樣的話,咱們就可以放心的將 Scoped 服務注入到中間件的構造函數中了。

接下來,咱們就來實現一個基于工廠的中間件:

public class YourMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // 下一個中間件處理之前的操作
        Console.WriteLine("YourMiddleware Begin");

        await next(context);

        // 下一個中間件處理完成后的操作
        Console.WriteLine("YourMiddleware End");
    }
}

public static class AppMiddlewareApplicationBuilderExtensions
{
    public static IApplicationBuilder UseYour(this IApplicationBuilder app) => app.UseMiddleware<YourMiddleware>();
}

然后,在ConfigureServices中添加中間件依賴注入

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<YourMiddleware>();
    }
}

最后,在Configure中使用中間件

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseYour();
    }
}

微軟提供了IMiddlewareFactory的默認實現:

public class MiddlewareFactory : IMiddlewareFactory
{
    // The default middleware factory is just an IServiceProvider proxy.
    // This should be registered as a scoped service so that the middleware instances
    // don't end up being singletons.
    // 默認的中間件工廠僅僅是一個 IServiceProvider 的代理
    // 該工廠應該注冊為 Scoped 服務,這樣中間件實例就不會成為單例
    private readonly IServiceProvider _serviceProvider;

    public MiddlewareFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IMiddleware? Create(Type middlewareType)
    {
        return _serviceProvider.GetRequiredService(middlewareType) as IMiddleware;
    }

    public void Release(IMiddleware middleware)
    {
        // The container owns the lifetime of the service
        // DI容器來管理服務的生命周期
    }
}

可以看到,該工廠使用過DI容器來解析出服務實例的。因此,當使用基于工廠的中間件時,是無法通過UseMiddleware向中間件的構造函數傳參的。

基于約定的中間件 VS 基于工廠的中間件

  • 基于約定的中間件實例都是 Singleton;而基于工廠的中間件實例可以是 Singleton、Scoped 和 Transient(當然,不建議注冊為 Singleton)

  • 基于約定的中間件實例構造函數中可以通過依賴注入傳參,也可以用過UseMiddleware傳參;而基于工廠的中間件只能通過依賴注入傳參

  • 基于約定的中間件實例可以在Invoke/InvokeAsync中添加更多的依賴注入參數;而基于工廠的中間件只能按照IMiddleware的接口定義進行實現。

關于“ASP.NET Core中間件Middleware怎么用”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

休宁县| 颍上县| 吉林省| 五家渠市| 建昌县| 蒲江县| 施甸县| 雅江县| 邵东县| 当涂县| 全椒县| 杂多县| 施甸县| 澜沧| 固安县| 仲巴县| 惠安县| 常宁市| 收藏| 从化市| 甘孜县| 沾益县| 长治县| 平阳县| 竹山县| 襄樊市| 武穴市| 东乡县| 高唐县| 长海县| 印江| 普安县| 广安市| 沅陵县| 濮阳市| 仲巴县| 西平县| 巨野县| 西和县| 巴东县| 乌苏市|