.NET CORE 中間件_網頁設計

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

什麼是中間件

對於中間件我們其實並不陌生,在.NET CORE出現之前中間件的概念在OWIN應用程序中就已經普遍使用了。
中間件官方定義: 中間件是一種集成到應用管道中間來處理請求和響應的模塊,每个中間件可以:

  • 選擇是否將請求傳遞到管道的下一個組件
  • 可以在管道的下一個組件前後執行工作

ASP.NETCORE中的中間件本質上是一個請求委託 Func< RequestDelegate, RequestDelegate> middleware
RequestDelegate本身也是一個委託,定義為 public delegate Task RequestDelegate(HttpContext Context)
在ASP.NETCORE請求管道中,形成一條委託鏈。

請求管道短路:當委託不選擇將請求傳遞到下一個委託時,稱之為“短路”。

如何創建中間件

在ASP.NETCORE中,使用 IApplicationBuilder 來創建/插入中間件管道。提供了 RunUse 兩類方式。依賴組件包 Microsoft.AspNetCore.Http.Abstractions
Run是一種 約定 的終端管道,即短路,不再執行下一個委託

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

		
        app.Run(async context => { await context.Response.WriteAsync("hello world 1"); });
		//這裏不會執行到!!
		app.Run(async context => { await context.Response.WriteAsync("hello world 2"); });

    }

Use通常以擴展方法提供中間件,很適合處理一些AOP的事務。

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.Use(async (context, next) =>
        {
            //可以在invoke之前做一些事
            await next.Invoke();
            //可以在invoke之後做一些事
        });

        app.Run(async context => { await context.Response.WriteAsync("hello world"); });
    }

實際開發中我們通常需要自己定義中間件,有兩種方式可以實現。

約定方式

public class RequestIdInRequestMiddleware
{
    private readonly RequestDelegate _next;

    public RequestIdInRequestMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public Task Invoke(HttpContext httpContext,IService service)
    {
		service.SayHello();
        //request head 加入requestid
        var requestId = Guid.NewGuid().ToString("n");
        httpContext.Request.Headers.Add("REQUESTID", requestId);

        return _next(httpContext);
    }
}

如上有以下約定:

  • 具有類型為 RequestDelegate 的參數公共構造函數
  • 名為 InvokeInvokeAsync 的公共方法,且此方法必須:
    • 返回 Task
    • 第一個參數為 HttpContext

目前官方是推薦使用約定方式, 注意:該方式加入管道中的生命周期為單例。也因此如果依賴一些Service,建議從InvokeInvokeAsync的方法參數注入,而不是從構造函數注入。(可以想想為什麼?單例構造函數注入對Service的生命周期有要求~~)。

強類型

官方也提供了IMiddleware接口,用於擴展創建中間件。這種方式有兩個優點:

網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

  • 可以按需(生命周期)注入

  • 中間件強類型話,更易理解

      public class RequestIdInResponseMiddleware:IMiddleware
      {
          private readonly IService _service;
    
          public RequestIdInResponseMiddleware(IService service)
          {
              _service = service;
          }
    
          public Task InvokeAsync(HttpContext context, RequestDelegate next)
          {
              var requestId = Guid.NewGuid().ToString("n");
              context.Response.Headers.Add("REQUESTID", requestId);
    
              return next(context);
          }
      }
    

中間件加入管道

中間件一般都是基於IApplicationBuilder擴展方法加入管道。

public static class RequestIdMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestIdInResponseMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestIdInResponseMiddleware>();
    }
}

可以在 Configure 方法中調用加入 app.UseRequestIdInResponseMiddleware();
如果是 強類型 方式創建的Middleware,還需要在 ConfigureServices 中註冊 services.AddSingleton<RequestIdInResponseMiddleware>();

中間件的順序

中間件顯著受加入的順序影響,官方提供的默認中間件順序圖

中間件分支Map

Map 擴展用來約定創建管道分支,和管道短路類似,不過它是基於給定的請求路徑匹配項來創建請求管道分支。官方提供的例子,

public class Startup
{
    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. <p>");
        });
    }
}

根據請求會響應不同結果

請求 響應
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

另外還可以使用 UseWhen 創建管道分支,只有匹配一定條件才會短路管道。

public void Configure(IApplicationBuilder app)
{
	//只有請求url包含查詢字符串變量 branch,才會短路管道
    app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
                builder => builder.Use(async (context, next) =>
                     {
                         var branchVer = context.Request.Query["branch"];
                         // Do work that doesn't write to the Response.
                         await next();
                         // Do other work that doesn't write to the Response.
                     }));

    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from main pipeline.");
    });
}

中間件的單元測試

針對中間件的單元測試,可以使用 TestServer 來進行。它有以下幾個優點:

  • 請求會發送到內存中,而不是通過網絡進行序列化
  • 避免產生額外的問題,例如端口號或Https等
  • 中間件中的異常可以直接流回調用測試
  • 可以直接在測試中自定義服務器數據結構,如 HttpContext

http請求發送模擬可以使用 HttpClientHttpContext ,分別可以驗證Response和Request Context相關功能。下面分別測試RequestIdInRequestMiddleware,RequestIdInResponseMiddleware。
新建xunit單元測試項目,加入依賴包: Microsoft.AspNetCore.TestHost , Microsoft.Extensions.Hosting
測試代碼如下:

public class MiddlewareTest
{
    /// <summary>
    /// HttpContext模擬,驗證request header是否成功加入requestId
    /// </summary>
    [Fact]
    public void MiddlewareTest_RequestHeaderExistRequestId()
    {
        var hostBuilder = new HostBuilder()
            .ConfigureWebHost(webBuilder =>
            {
                webBuilder
                    .UseTestServer()
                    .ConfigureServices((context, services) =>
                    {
                        services.AddTransient<IService, MyService>();
                    })
                    .Configure(app =>
                    {
                        app.UseRequestIdInRequestMiddleware();
                    });
            });
        using (var host = hostBuilder.Start())
        {
            var context = host.GetTestServer().SendAsync(c =>
                    {
                        c.Request.Path = "/map";
                        c.Request.Method = HttpMethods.Get;
                    }).Result;

            Assert.True(context.Request.Headers.ContainsKey("REQUESTID"));
        }
    }
    /// <summary>
    /// HttpClient模擬,驗證response header是否成功加入requestId
    /// </summary>
    [Fact]
    public void MiddlewareTest_ResponseHeaderExistRequestId()
    {
        var hostBuilder = new HostBuilder()
            .ConfigureWebHost(webBuilder =>
            {
                webBuilder
                    .UseTestServer()
                    .ConfigureServices((context, services) =>
                    {
                        services.AddSingleton<RequestIdInResponseMiddleware>();
                        services.AddTransient<IService, MyService>();
                    })
                    .Configure(app =>
                    {
                        app.UseRequestIdInResponseMiddleware();
                    });
            });
        using (var host = hostBuilder.Start())
        {
            host.GetTestServer().CreateRequest("/map").GetAsync()
                .ContinueWith(task =>
                {
                    var response = task.Result;
                    Assert.True(response.Headers.Contains("REQUESTID"));
                }).Wait();
        }
    }
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品