ASP.NET Core 中介軟體詳解及專案實戰

Savorboard發表於2016-06-15

前言

上篇文章主要介紹了DotNetCore專案狀況,本篇文章是我們在開發自己的專案中實際使用的,比較貼合實際應用,算是對中介軟體的一個深入使用了,不是簡單的Hello World,如果你覺得本篇文章對你有用的話,不妨點個【推薦】。

目錄

  • 中介軟體(Middleware)的作用
  • 中介軟體的執行方式
  • 中介軟體(Middleware)和過濾器(Filter)的區別
  • 什麼情況我們需要中介軟體
  • 怎麼樣自定義自己的中介軟體

中介軟體(Middleware)的作用

我們知道,任何的一個web框架都是把http請求封裝成一個管道,每一次的請求都是經過管道的一系列操作,最終到達我們寫的程式碼中。那麼中介軟體就是在應用程式管道中的一個元件,用來攔截請求過程進行一些其他處理和響應。中介軟體可以有很多個,每一箇中介軟體都可以對管道中的請求進行攔截,它可以決定是否將請求轉移給下一個中介軟體。

asp.net core 提供了IApplicationBuilder介面來讓把中介軟體註冊到asp.net的管道請求當中去,中介軟體是一個典型的AOP應用。 下面是一個微軟官方的一箇中介軟體管道請求圖:

image

可以看到,每一箇中介軟體都都可以在請求之前和之後進行操作。請求處理完成之後傳遞給下一個請求。

中介軟體的執行方式

預設情況下,中介軟體的執行順序根據Startup.cs檔案中,在public void Configure(IApplicationBuilder app){} 方法中註冊的先後順序執行。
大概有3種方式可以在管道中註冊"中介軟體"

  1. app.Use()IApplicationBuilder介面原生提供,註冊等都用它。
  2. app.Run() ,是一個擴充套件方法,它需要一個RequestDelegate委託,裡面包含了Http的上下文資訊,沒有next引數,因為它總是在管道最後一步執行。
  3. app.Map(),也是一個擴充套件方法,類似於MVC的路由,用途一般是一些特殊請求路徑的處理。如:www.example.com/token 等。

上面的Run,Map內部也是呼叫的Use,算是對IApplicationBuilder介面擴充,如果你覺得名字都不夠準確,那麼下面這個擴充套件方法就是正宗的註冊中介軟體的了,也是功能最強大的。
app.UseMiddleware<>(),沒錯,就是這個了。 為什麼說功能強大呢?是因為它不但提供了註冊中介軟體的功能,還提供了依賴注入(DI)的功能,以後大部分情況就用它了。

中介軟體(Middleware)和過濾器(Filter)的區別

熟悉MVC框架的同學應該知道,MVC也提供了5大過濾器供我們用來處理請求前後需要執行的程式碼。分別是AuthenticationFilter,AuthorizationFilter,ActionFilter,ExceptionFilter,ResultFilter

根據描述,可以看出中介軟體和過濾器的功能類似,那麼他們有什麼區別?為什麼又要搞一箇中介軟體呢?
其實,過濾器和中介軟體他們的關注點是不一樣的,也就是說職責不一樣,乾的事情就不一樣。

舉個栗子,中介軟體像是埃辛諾斯戰刃,過濾器像是巨龍之怒,泰蕾苟薩的寄魂杖 ,你一個戰士拿著巨龍之怒,泰蕾苟薩的寄魂杖去戰場殺人,雖然都有傷害,但是你拿著法杖傷害低不說,還減屬性啊。

同作為兩個AOP利器,過濾器更貼合業務,它關注於應用程式本身,比如你看ActionFilterResultFilter,它都直接和你的Action,ActionResult互動了,是不是離你很近的感覺,那我有一些比如對我的輸出結果進行格式化啦,對我的請求的ViewModel進行資料驗證啦,肯定就是用Filter無疑了。它是MVC的一部分,它可以攔截到你Action上下文的一些資訊,而中介軟體是沒有這個能力的。

什麼情況我們需要中介軟體

那麼,何時使用中介軟體呢?我的理解是在我們的應用程式當中和業務關係不大的一些需要在管道中做的事情可以使用,比如身份驗證,Session儲存,日誌記錄等。其實我們的 asp.net core專案中本身已經包含了很多箇中介軟體。

舉例,我們在新建一個 asp.net core應用程式的時候,預設生成的模板當中

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    app.UseDeveloperExceptionPage();
    
    app.UseStaticFiles();
    
    loggerFactory.AddConsole();
    
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

懶得去下載原始碼了,我們使用Reflector去檢視原始碼:

//擴充套件方法`app.UseDeveloperExceptionPage();`
public static class DeveloperExceptionPageExtensions
{
    // Methods
    public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException("app");
        }
        return UseMiddlewareExtensions.UseMiddleware<DeveloperExceptionPageMiddleware>(app, Array.Empty<object>());
    }
}

//擴充套件方法`app.UseStaticFiles();`
public static class StaticFileExtensions
{
   // Methods
   public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException("app");
        }
        return UseMiddlewareExtensions.UseMiddleware<StaticFileMiddleware>(app, Array.Empty<object>());
    }
}

可以看到 app.UseDeveloperExceptionPage()app.UseStaticFiles()等等都是通過中介軟體實現的。

怎麼樣自定義自己的中介軟體

背景:我們專案使用到中介軟體的情景是,需要和其他部門進行使用者(User)資訊的共享。 以平臺和子系統舉例,我們正在開發一個子系統,其中使用者資訊,登入,註冊等功能是放在平臺上的,這是一個跨多語言的系統,平臺是Java語言開發,使用者在訪問子系統的一些頁面的時候需要驗證是否登入,另外一些頁面是不需要驗證是否登入的,所以需要一個身份驗證系統來代替Identity的功能。

幸運的是微軟已經給我們提供了一套身份驗證的中介軟體,在Microsoft.AspNetCore.Authentication名稱空間下,我們只需要擴充,新增自己的功能就行了 。具體怎麼做呢?直接看程式碼吧。

根據約定俗成,中介軟體類需要有一個Invoke方法,簽名是public async Task Invoke(HttpContext context){},下面是一箇中介軟體的示例類:

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.");
    }
}

瞭解了上面的約定之後,我們就開始定義我們自己的中介軟體Class。

我們需要一個流程圖來理清邏輯思路,以便於寫程式碼的時候思路更加的清晰。

image

平臺有一個要求就是,使用者在我們子系統退出之後,要呼叫平臺的一個介面通知他們,他們要做一些後續的業務。

OK,開始擼碼。

  • 首先建立一個PlatformAuthoricationMiddleware,它繼承於Microsoft.AspNetCore.Authentication下的類AuthenticationMiddleware,由於AuthenticationMiddleware已經實現了Invoke功能,所以我們只需要重寫(override)它裡面的一些方法就可以了。等等,我們好像還需要一些配置,比如流程圖中的ReturnUrl,平臺的Cookie的Key值,平臺驗證使用者合法性的介面地址等引數。
  • 建立一個Options類進行配置的設定,我們取名字為:PlatformAuthenticationOptions,繼承AuthenticationOptions,並且實現掉IOptions<T>介面,這樣子就能在Startup中直接配置了。
  • 我們只需要重寫AuthenticationMiddleware中的CreateHandler方法就行了,在Handler中可以實現掉我們中介軟體的功能。
  • 然後建立一個處理的Handler類,取名為PlatformAuthenticationHandler,繼承於AuthenticationHandler<TOptions>用來處理請求中的呼叫。

至此,我們的核心需要的類已經建立完了,剩下的就是填充程式碼。

  1. PlatformAuthenticationHandler中重寫HandleAuthenticateAsync()方法 , 進行主流程的控制。
  2. PlatformAuthenticationHandler中重寫FinishResponseAsync()方法,進行Session的儲存操作。
  3. PlatformAuthenticationHandler中重寫HandleSignOutAsync()方法,進行登出的控制,因為使用者登出之後我們要通知平臺做一些其他操作。
  4. PlatformAuthenticationHandler中重寫HandleUnauthorizedAsync()方法,進行未認證操作。

最後,我們需要一個擴充套件類來把我們的中介軟體以擴充套件方法註冊到管道當中去 。

public static class MiddlewareExtensions
{
    public static IApplicationBuilder UsePlatformAuthentication(this IApplicationBuilder app) {
        if (app == null) {
            throw new ArgumentNullException(nameof(app));
        }

        return app.UseMiddleware<PlatformAuthenticationMiddleware>();
    }

    public static IApplicationBuilder UsePlatformAuthentication(this IApplicationBuilder app, CookieAuthenticationOptions options) {
        if (app == null) {
            throw new ArgumentNullException(nameof(app));
        }
        if (options == null) {
            throw new ArgumentNullException(nameof(options));
        }

        return app.UseMiddleware<PlatformAuthenticationMiddleware>(Options.Create(options));
    }
}

Startup中就是app.UsePlatformAuthentication()

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

    //註冊PlatformAuthentication中介軟體
    app.UsePlatformAuthentication(new PlatformAuthenticationOptions() {
        UserSessionStore = new UserSessionStore(),
    });

    app.UseMvc();
}

現在,我們的中介軟體核心業務流程的實現已經出來了,我就不大篇幅的貼上程式碼了,會影響閱讀,感興趣具體實現的朋友可以去下面的地址檢視程式碼,有具體流程的註釋。

示例原始碼:
https://github.com/yuleyule66/PlatformAuthMiddleware


本文地址:http://www.cnblogs.com/savorboard/p/5586229.html
作者部落格:Savorboard
歡迎轉載,請保留出處

相關文章