ASP.NET 5基礎之中間件

來源https://docs.asp.net/en/latest/fundamentals/middleware.htmlhtml

一些能夠整合進http請求管道的小的應用組件稱作中間件。ASP.NET 5集成支持中間件,能夠經過應用的Configure方法來配置中間件。web

什麼是中間件

中間件是一些能夠裝配在應用請求管道中的一些組件,能夠用來處理請求和響應。每一箇中間件能夠以爲是否將請求轉給管道中的下一個組件,也能夠在下一個組件前或後執行一些操做。Request delegates用來構建請求管道,而後處理每個到應用的請求。json

Request delegates是經過IApplicationBuilder類型的Run,Map,Use擴展方法來配置,IApplicationBuilder是在Startup類中傳給Configure方法的。request delegate能夠是內嵌的匿名方法,也能夠是定義在一個可服用的類中。這寫可服用的類就是中間件,管道中的每一箇中間件都負責invoke管道中的下一個中間件,或者在適當的狀況下之間短路管道中的request delegate鏈(直接返回)。服務器

經過IApplicationBuilder來建立中間件管道

ASP.NET請求管道由一系列的請求代理(request delegate)組成,一個接一個的被調用。以下圖所示,黑色箭頭標識執行線程
pipelinesession

每一個代理都在能夠在下一個代理以前和以後執行一些操做。任何一個代理能夠選擇是將請求轉給下一個代理仍是本身進行處理。直接處理就是短路執行管道,這樣能夠在某些狀況下避免執行無謂的操做。好比受權組件,能夠在受權經過的狀況下將請求轉個下一個組件,或者在未受權的狀況下直接返回未受權。還有異常處理組件,要在管道的較前方來調用,這樣才能捕獲後面組件鏈上拋出的異常。
在默認的web模板中,你能夠看到用請求代理來配置管道的例子,以下Configure方法,首先wire up 錯誤頁面(in development)或者網站的生產環境的error handler,而後構建了支持static files, ASP.NET Identity authentication, and finally, ASP.NET MVC的管道。app

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseBrowserLink();
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

    app.UseStaticFiles();

    app.UseIdentity();

    // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

由於管道是按順序構建的,在生產環境下UseExceptionHandler配置在了管道的較前方,因此可以捕獲管道上稍後被調用的組件拋出的異常。並且在這裏,UseStaticFiles是在UseIdentity以前配置的,這樣靜態文件就不須要受權認證,這樣能夠提升性能(ASP.NET 5中靜態文件都在wwwroot,默認是均可以訪問的,是不須要執行受權組件的 )。對與非靜態文件的請求則能夠流轉到管道中的下一個組件。 Learn more about Working with Static Files.asp.net

Note: 在Configure方法中配置的中間件Use[Middleware],順序很重要。async

最簡單的ASP.NET應用就是設置一個請求代理來處理全部請求。在這種狀況下沒有「真正的」請求管道,由於只有一個匿名函數來處理全部請求。ide

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello, World!");
});

這裏很重要的一點是,如上所寫的請求代理會終結管道,無論你是否有別的App.Run調用。在下面的例子中,只有第一個代理會被調用。函數

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

    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello, World, Again!");
    });

能夠經過next調用來將多個request代理連接起來,這裏next參數表明管道中的下一個請求代理。注意,雖然你調用了next,但並不意味你不能在下個請求代理前或者後來執行操做,以下例所示。

public void ConfigureLogInline(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
    loggerfactory.AddConsole(minLevel: LogLevel.Information);
    var logger = loggerfactory.CreateLogger(_environment);
    app.Use(async (context, next) =>
    {
        logger.LogInformation("Handling request.");
        await next.Invoke();
        logger.LogInformation("Finished handling request.");
    });

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

警告: Be wary of modifying HttpResponse after invoking next, since one of the components further down the pipeline may have written to the response, causing it to be sent to the client.(在invoke next以後儘可能不要修改HttpResponse,由於管道後面的組件可能已經寫入了response)
Note: 環境設置爲LogInline,ConfigureLogInline方法會在應用運行時調用。更多見Working with Multiple Environments.在剩下的文章中,咱們可以使用Configure[Environment]來顯示不一樣的選項。最簡單的運行樣例的方式是使用web命令,它是在project.json中配置的。

在上面的例子中,await next.Invoke() 會使程序進入到第14行,客戶端收到預期的響應「Hello from LogInline」,而後服務器控制檯輸出組件前和組件後的消息,以下所示。
console

Run, Map, and Use

能夠經過擴展方法Run, Map, and Use來配置管道。按照約定,Run是一種簡便的添加中間件到管道的方法,而且不會調用其餘剩餘中間件。因此Run應該在管道的最後被調用。Run是一種約定,一些中間件暴露出它的Run[Middleware]方法,這些方法應該只在管道的末端調用。以下例子(one using Run and the other Use)是等價的,由於第二個沒有調用next方法。

public void ConfigureEnvironmentOne(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from " + _environment);
    });
}

public void ConfigureEnvironmentTwo(IApplicationBuilder app)
{
    app.Use(next => async context =>
    {
        await context.Response.WriteAsync("Hello from " + _environment);
    });
}

Note: IApplicationBuilder接口自己暴露出了Use方法,因此技術上他們不全是擴展方法。
關於Use的例子,咱們已經看了好幾個。**Map*擴展方法是一種branching the pipeline的約定。當前的實現支持基於請求路徑的分支和使用斷言的(predicate)**。Map擴展方法能夠用來基於請求路徑來匹配請求代理。Map 僅僅接受路徑和配置單獨中間件的管道的函數。以下例所示,根路徑爲/base path的請求,都會由HandleMapTest方法中配置的管道處理。

private static void HandleMapTest(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test Successful");
    });
}

public void ConfigureMapping(IApplicationBuilder app)
{
    app.Map("/maptest", HandleMapTest);

}

Note: 當使用Map時,對每一個請求,匹配的path segment會從HttpResponse.Path中移除,並添加到HttpRequest.PathBase.

除了基於路徑的匹配,MapWhen支持基於斷言的管道分支,運行靈活的構建一個seperate管道。任何知足Func<HttpContext, bool>的斷言,均可以使用,將請求map到一個新的管道。在下例中,一個簡單的斷言來檢查請求參數branch

private static void HandleBranch(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Branch used.");
    });
}

public void ConfigureMapWhen(IApplicationBuilder app)
{
    app.MapWhen(context => {
        return context.Request.Query.ContainsKey("branch");
    }, HandleBranch);

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

在此例中任何知足斷言的請求都會由定義在HandleBranch中的管道來處理。其餘的請求在會有17行的定義的代理來處理。

Built-in middleware(內建中間件)

ASP.NET 內有以下自帶中間件

Middleware Description
Authentication Provides authentication support.
CORS Configures Cross-Origin Resource Sharing.
Diagnostics Includes support for error pages and runtime information.
Routing Define and constrain request routes.
Session Provides support for managing user sessions.
Static Files Provides support for serving static files, and directory browsing.

Writing middleware(編寫中間件)

對於較附加的請求處理函數,建議使用單獨的類來構建中間件,經過暴露出能夠在Configure中調用的IApplicationBuilder的擴展方法。把以前簡單的logging 中間件轉換到單獨的一個類中,並把RequestDelegate 做爲構造器參數,支持Invoke方法,以下。

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Logging;
using System.Threading.Tasks;

namespace MiddlewareSample
{
    public class RequestLoggerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public RequestLoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
        {
            _next = next;
            _logger = loggerFactory.CreateLogger<RequestLoggerMiddleware>();
        }

        public async Task Invoke(HttpContext context)
        {
            _logger.LogInformation("Handling request: " + context.Request.Path);
            await _next.Invoke(context);
            _logger.LogInformation("Finished handling request.");
        }
    }
}

中間件遵循 Explicit Dependencies Principle。並經過構造器暴露出全部依賴。中間件利用 UseMiddleware 擴展來直接注入所依賴服務,以下。依賴注入的服務是自動填充的,the extension takes a params array of arguments to be used for non-injected parameters。

public static class RequestLoggerExtensions
{
    public static IApplicationBuilder UseRequestLogger(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestLoggerMiddleware>();
    }
}

使用擴展方法和相關連的中間件類,使得Configure方法具備良好的可讀性。

public void ConfigureLogMiddleware(IApplicationBuilder app,
    ILoggerFactory loggerfactory)
{
    loggerfactory.AddConsole(minLevel: LogLevel.Information);

    app.UseRequestLogger();

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

儘管RequestLoggerMiddleware須要構造器參數ILoggerFactory,Startup類和UseRequestLogger 擴展方法都沒顯式提供。可是經過UseMiddleware 內的依賴注入操做自動提供的。
Testing the middleware (by setting the ASPNET_ENV environment variable to LogMiddleware) should result in output like the following (when using WebListener):
logging

Note:另外一個是用UseMiddleware 的例子是 UseStaticFiles擴展方法,它使用必須的構造器參數來建立StaticFileMiddleware 。在這個例子中,StaticFileOptions參數是傳進來的,別的構造器參數則是UseMiddleware 依賴注入提供的。

Summary

Middleware provide simple components for adding features to individual web requests. Applications configure their request pipelines in accordance with the features they need to support, and thus have fine-grained control over the functionality each request uses. Developers can easily create their own middleware to provide additional functionality to ASP.NET applications.

相關文章
相關標籤/搜索