ASP.NET Core筆記(5) - 中間件

  • 中間件管道模型
  • 中間件的配置
  • 自定義中間件
  • 中間件是一類裝配在應用管道的代碼,負責處理請求和響應。每個中間件都可在管道中的下一個組件前後執行工作,並選擇是否將請求傳遞到管道中的下一個中間件。在Startup.Configure方法中可以進行中間件的裝配。

    中間件管道模型

    中間件管道模型如下圖所示:

    ASP.NET Core筆記(5) - 中間件

    ASP.NET Core請求管道包含一系列請求委託,沿黑色箭頭依次被調用執行,每個委託均可在下一個委託前後執行操作。這種模型也被形象地稱為“俄羅斯套娃”。

    一箇中間件可以是匿名方法的顯示嵌入到管道中,也可以封裝為單獨的類便於重用,嵌入式的中間件就像這樣:

    <code>

    public

    void

    Configure

    (

    IApplicationBuilder app

    )

    { app.Run(

    async

    context => {

    await

    context.Response.WriteAsync(

    "Hello, World!"

    ); }); } /<code>

    中間件的配置

    配置中間件會使用到三個擴展方法:

    • Use
    • Run
    • Map

    Use

    Use用來將多箇中間件按添加順序鏈接到一起:

    <code>app.Use(

    async

    (context, next) => {

    await

    context.Response.WriteAsync(

    "middleware1 begin\r\n"

    );

    await

    next.Invoke();

    await

    context.Response.WriteAsync(

    "middleware1 end\r\n"

    ); }); app.Use(

    async

    (context, next) => {

    await

    context.Response.WriteAsync(

    "middleware2 begin\r\n"

    );

    await

    next.Invoke();

    await

    context.Response.WriteAsync(

    "middleware2 end\r\n"

    ); }); app.Run(

    async

    context => {

    await

    context.Response.WriteAsync(

    "end of pipeline.\r\n"

    ); }); /<code>

    這三個中間件配置到管道後,輸出的結果與管道模型的圖示是一致的:

    <code>

    middleware1

    begin

    middleware2

    begin

    end

    of pipeline.

    middleware2

    end

    middleware1

    end

    /<code>

    可以看到除了最後一箇中間件,前面的中間件都調用了await next.Invoke(),next參數表示管道中的下一個委託,如果不調用 next,後面的中間件就不知執行,這稱為管道的短路。通常中間件都應該自覺調用下一個中間件,但有的中間件會故意造成短路,比如授權中間件、靜態文件中間件等。

    Run

    Run的委託中沒有next參數,這就意味著它會稱為最後一箇中間件(終端中間件),此外可以故意短路的Use委託也可能會成為終端中間件。

    Map,提供了創建管道分支的能力

    Map擴展用作約定來創建管道分支,會基於給定請求路徑的匹配項來創建請求管道分支,如果請求路徑以給定路徑開頭,則執行分支。

    <code>

    private

    static

    void

    HandleMapTest1

    (

    IApplicationBuilder app

    )

    { app.Run(

    async

    context => {

    await

    context.Response.WriteAsync(

    "Map Test 1"

    ); }); }

    private

    static

    void

    HandleMapTest2

    (

    IApplicationBuilder app

    )

    { app.Run(

    async

    context => {

    await

    context.Response.WriteAsync(

    "Map Test 2"

    ); }); }

    public

    void

    Configure

    (

    IApplicationBuilder app

    )

    { app.Map(

    "/map1"

    , HandleMapTest1); app.Map(

    "/map2"

    , HandleMapTest2); app.Run(

    async

    context => {

    await

    context.Response.WriteAsync(

    "Hello from non-Map delegate.

    /<code>

    這段代碼為管道創建了三個分支:

    URLResponselocalhost:1234Hello from non-Map delegate.localhost:1234/map1Map Test 1localhost:1234/map2Map Test 2

    Map還支持嵌套
    <code>app.Map(

    "/level1"

    ,

    level1App

    =>

    { level1App.Map(

    "/level2a"

    ,

    level2AApp

    =>

    { }); level1App.Map(

    "/level2b"

    ,

    level2BApp

    =>

    { }); }); /<code>
    MapWhen,滿足條件時創建分支
    <code>app.MapWhen(

    context

    =>

    context.Request.Query.ContainsKey(

    "branch"

    ),

    branch

    =>

    { }); /<code>

    此外,UseWhen也可以根據條件創建分支,它與MapWhen的區別在於,使用UseWhen創建的分支如果不包含終端中間件,則會重新匯入主管道。

    自定義中間件

    通常使用內置的中間件可滿足大多數場合,但如果有需要也可以創建自定義中間件。假設有這樣一個嵌入式中間件,可以通過查詢字符串設置當前請求的區域,比如https://localhost:5000/?culture=zh-cn,會將CurrentCulture設置為Chinese Simplified。現在要將其封裝為可重用的獨立的中間件。

    <code>

    public

    void

    Configure

    (

    IApplicationBuilder app

    )

    { app.Use(

    async

    (context, next) => {

    var

    cultureQuery = context.Request.Query[

    "culture"

    ];

    if

    (!

    string

    .IsNullOrWhiteSpace(cultureQuery)) {

    var

    culture =

    new

    CultureInfo(cultureQuery); CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; }

    await

    next(); }); app.Run(

    async

    (context) => {

    await

    context.Response.WriteAsync(

    $"Hello

    {CultureInfo.CurrentCulture.DisplayName}

    "

    ); }); } /<code>

    在開始封裝前,先了解一下對中間件的要求:

    • 中間件必須具有類型為RequestDelegate的參數的公共構造函數,之前代碼中看到next.Invoke(),其中next就是下一個中間件的RequestDelegate;
    • 名為 Invoke 或 InvokeAsync 的公共方法。而且這個方法的返回類型為Task,且第一個參數的必須是HttpContext,其它的參數由DI容器解析。

    基於上述要求,編寫的中間件為:

    <code>

    public

    class

    RequestCultureMiddleware

    {

    private

    readonly

    RequestDelegate _next;

    public

    RequestCultureMiddleware

    (

    RequestDelegate next

    )

    { _next = next; }

    public

    async

    Task

    InvokeAsync

    (

    HttpContext context

    )

    {

    var

    cultureQuery = context.Request.Query[

    "culture"

    ];

    if

    (!

    string

    .IsNullOrWhiteSpace(cultureQuery)) {

    var

    culture =

    new

    CultureInfo(cultureQuery); CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; }

    await

    _next(context); } } /<code>

    然後就可以使用了:

    <code>

    app

    .UseMiddleware

    <

    RequestCultureMiddleware

    >(); /<code>

    還可以進一步封裝為IApplicationBuilder的擴展方法:

    <code>

    public

    static

    class

    RequestCultureMiddlewareExtensions

    {

    public

    static

    IApplicationBuilder

    UseRequestCulture

    (

    this

    IApplicationBuilder builder)

    {

    return

    builder.UseMiddleware(); } } /<code>

    然後就可以像內置的中間件一樣了:

    <code>

    app

    .UseRequestCulture

    ();/<code>


    原文地址:https://www.cnblogs.com/zhixin9001/p/12778964.html


    分享到:


    相關文章: