1. 程式人生 > >ASP.NET Core 中介軟體的幾種實現方式

ASP.NET Core 中介軟體的幾種實現方式

前言

ASP.NET Core 中 HTTP 管道使用中介軟體組合處理的方式,

換句人話來說,

對於寫程式碼的人而言,一切皆中介軟體.

業務邏輯/資料訪問/等等一切都需要以中介軟體的方式來呈現.

那麼我們必須學會如何實現自定義中介軟體 這裡劃重點,必考

這裡我們介紹下中介軟體的幾種實現方式...

匿名函式

通常新建一個空的 ASP.NET Core Web Application,專案名字無所謂啦

在啟動類裡可以看到這麼一句:

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

這就是一個匿名函式實現的中介軟體,雖然內容比較少.

可以看到通過匿名函式實現的中介軟體是內嵌在啟動類檔案中的,因此通常也叫做內聯中介軟體

接下來,我們通過匿名函式來實現內聯中介軟體,以便加深理解.

新建一個空的 ASP.NET Core Web Application

然後修改啟動類程式碼如下:

// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace WebApplication1
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

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

            // 使用匿名函式實現一個內聯中介軟體
            app.Use(async (context, next) =>
            {
                throw new NotImplementedException("一個使用匿名函式,但未實現具體內容的內聯中介軟體");
            });

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

這裡我們在 app.Run 之前使用 app.Use 新增一個匿名函式實現的內聯中介軟體,按照中介軟體的註冊順序,當發起請求時,會丟擲一個異常 NotImplementedException("一個使用匿名函式,但未實現具體內容的內聯中介軟體")

我們 F5 啟動下,看看頁面

嗯,符合預期.

我們再來調整下啟動類,程式碼如下:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

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

            // 使用匿名函式實現一個內聯中介軟體
            app.Use(async (context, next) =>
            {
                // 這裡不對 request 做任何處理,直接呼叫下一個中介軟體
                await next.Invoke();
            });

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

這裡我們在 app.Run 之前使用 app.Use 新增一個匿名函式實現的內聯中介軟體,該中介軟體沒有對 request 做任何處理,只是一個空的空間件,按照中介軟體的註冊順序,當發起請求時,頁面應該顯示 Hello World!.

我們 F5 啟動,看看效果

嗯,符合預期.

個人覺得:匿名函式不是很直觀,但是用內聯的方式可以快速開始一些開發,不用新建一箇中間件類,不用專門想個不一樣的名字,小場景下是非常方便實用的

實現介面

通過實現介面 IMiddleware 編寫自定義中介軟體,這是一種強型別的方式,我們需要必須強制按照介面的定義來實現.

IMiddleware

介面 IMiddleware 定義如下:

using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Http
{
    public interface IMiddleware
    {
        Task InvokeAsync(HttpContext context, RequestDelegate next);
    }
}

可以看到介面 IMiddleware 的名稱空間是 Microsoft.AspNetCore.Http,需要實現的方法是InvokeAsync(),看起來不算太複雜, 嗯,看起來不算太複雜

嗯,重新開始,我們新建一個空的 ASP.NET Core Web Application

然後我們通過實現介面的方式來自定義一箇中間件,程式碼如下:

// 新建類 MyMiddleware.cs
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication1
{
    public class MyMiddleware : IMiddleware
    {
        public Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            throw new NotImplementedException();
        }
    }
}

按照上面實現的中介軟體 MyMiddleware,在執行時應該會丟擲 NotImplementedException.

使用介面實現的中介軟體需要在先在服務容器中註冊

// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // 在服務容器中註冊自定義中介軟體
            services.AddSingleton<MyMiddleware>();
        }

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

            // 使用 UseMiddleware() 把自定義中介軟體新增到管道中
            app.UseMiddleware<MyMiddleware>();

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

然後 F5 啟動,頁面上可以看到如下結果:

符合我們上面的預期,丟擲了一個 NotImplementedException.

然後我們改造下 MyMiddleware 中介軟體

// MyMiddleware.cs
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace WebApplication1
{
    public class MyMiddleware : IMiddleware
    {
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            // 這裡不對 request 做任何處理,直接呼叫下一個中介軟體
            await next(context);
        }
    }
}

這裡相當於我們實現了一個叫做 MyMiddleware 的中介軟體,但是並沒有對請求進行任何處理,頁面上應該正常顯示 Hello World! 字串.

然後我們 F5 啟動看看

嗯...符合預期.

個人覺得:這種方式最符合面向物件的特性,也符合面向介面的原則,少一些難以理解的魔法,反而有助於理解.

約定方式

程式設計世界有這麼一句話,叫"約定大於配置".

那麼編寫中介軟體的約定是什麼呢?

重新開始,新建一個空的 ASP.NET Core Web Application

然後新建一個類,類名叫做 MyMiddleware 好了,程式碼如下:

// MyMiddleware.cs
using Microsoft.AspNetCore.Http;
using System;
using System.Threading.Tasks;

namespace WebApplication1
{
    public class MyMiddleware
    {
        // 1. 需要實現一個建構函式,引數為 RequestDelegate
        public MyMiddleware(RequestDelegate next)
        {

        }

        // 2. 需要實現一個叫做 InvokeAsync 方法
        public async Task InvokeAsync(HttpContext context)
        {
            throw new NotImplementedException("這是一個按照約定方式編寫的中介軟體,但未實現具體內容");
        }
    }
}

約定的內容,就是滿足2個需要...不滿足需要則異常.

然後我們把這個中介軟體,註冊到管道中,以便使用

// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

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

            // 註冊自定義中介軟體
            // 註冊順序=1
            app.UseMiddleware<MyMiddleware>();

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

然後 F5 啟動,來看看效果

嗯,符合預期.

然後我們來調整下中介軟體,讓請求能正常響應輸出 Hello World!

using Microsoft.AspNetCore.Http;
using System;
using System.Threading.Tasks;

namespace WebApplication1
{
    public class MyMiddleware
    {
        private readonly RequestDelegate _next;

        // 需要實現一個建構函式,引數為 RequestDelegate
        public MyMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        // 需要實現一個叫做 InvokeAsync 方法
        public async Task InvokeAsync(HttpContext context)
        {
            // 不處理任何 request, 直接呼叫下一個中介軟體
            await _next.Invoke(context);
        }
    }
}

然後 F5 啟動,看看效果

嗯,符合預期.

個人覺得:只能說一句,約定方式是目前用的最多的方式...

End

寫在最後

Tips: 有些內容可能看起來還是不太容易理解,至少當下你是很難理解的,但是套路就在哪裡,好比1+1=2,你知道1+1為什麼=2麼?但你一定會算會用1+1=2...