.NET 6 Minimal API 的經驗分享

微軟技術棧發表於2022-05-15

Minimal API 是 .NET 6 提供的最新功能 , 對比傳統的 http://ASP.NET Core Web API 方式更加直接 , 你可以用幾行程式碼編寫好 REST API 。沒有了祖傳的 Startup.cs 和 Controller ,通過簡單的程式碼就可以完成 API 的開發。在第二階段的 .NET 挑戰賽中就以 .NET 6 中 的 Minimal API 作為學習的主線來完成相關的雲原生應用。有小夥伴問怎麼可以用好 Minimal API ,如何去架構一個 Minimal API 的雲原生解決方案 ,下面我和大家說說 。

再來認識一下 Minimal API


對比起傳統的 Web API , Minimal API 取消了 Controller , 檔案的組織方式更像 Node.js . 在以前要啟動一個 Web API , .NET 對比起 JavaScript , Go , Rust , Python 等語言的 Web 框架還是相對複雜的。.NET 團隊希望通過 Minimal API 簡化 .NET Web 框架 ,讓開發者能在一個檔案完成簡單 API 構建。以下是一個最基本的 Minimal API 專案。

MapGet 是 EndpointRouteBuilderExtensions 類的擴充套件方法, 你可以看到它設定了路由規則後, 會傳遞一個委託引數 , 編譯器會將它轉換為 RequestDelegate ,通過它來取代 Controller 的工作。然後使用 app.Run() 方法來執行我們的 Minimal API 應用程式。

架構 .NET 6 Minimal API 專案

在傳統的 Web API 通過 Controller ,針對不同的功能進行 CRUD 的開發, 但如果我們是 Minimal API 呢 , 要如何組織呢 ?還有我們是否還能用 Repository Pattern 呢 ?

  1. Controller 再見

假設我們要搭建一個關於課程資訊的 API, 有課程基本資訊(課程型別,課程列表)和選課資訊(學生選課,每門課學生選課資訊),如果從傳統 Web API 你需要新增 CourseController 和 BookController 兩個控制器,通過不同 Action 去對應相關路由 ,但對於 Minimal API 如果我們涉及很多的路由就會讓我們的 Program.cs 檔案過大,非常不好管理。那我們把功能點切分,或者說重新模擬 Controller 去劃分還是非常好的。

如上圖 ,是我畫的一個圖 ,通過把 Course 和 Book 劃分為兩個 Module , 在 Module 裡都包含了路由的設定 。這個時候我們做一個介面 ,因為每個 Module 都需要包含新增路由的方法,所以把它做成一個介面 IBaseModule ,以後不同的 Module 都可以用 。

如何新增進去 Program.cs ? 我們來用一個 IEndpointRouteBuilder 的擴充套件方法 ,把路由新增都歸類進來

public static class ModuleExtensions
{
​
    public static void Routers(this IEndpointRouteBuilder builder)
    {
​
        CourseModule courseModule = new CourseModule();
        BookModule bookModule = new BookModule();
        courseModule.AddModuleRoutes(builder);
        bookModule.AddModuleRoutes(builder);
​
​
    }
}

這裡有一個技巧就是我們可以通過以下 typeof 找到繼承於 IBaseModule 的 Module 並將它例項化後呼叫新增路由的方法。

    public static void Routers(this IEndpointRouteBuilder builder)
    {
​
        var modules = typeof(IBaseModule).Assembly
            .GetTypes()
            .Where(p => p.IsClass && p.IsAssignableTo(typeof(IBaseModule)))
            .Select(Activator.CreateInstance)
            .Cast<IBaseModule>();
​
        foreach(var module in modules)
        {
            module.AddModuleRoutes(builder);
        }
​
    }
  1. Repostitory 模式能延續嗎 ?

這是毫無疑問可以繼續使用。通過 Repository 模式你不需要去關心你所使用的 ORM 是什麼 , 與 ORM 的所有處理過程都在 Repository 層中處理掉 , 通過這樣去減少耦合, 提供更好的可測試性。在微軟文件中,有以下的比較

在 Minimal API 使用 Repository Pattern 在整體上和傳統的 http://ASP.NET MVC 沒有不同 ,這裡是我用 Repository 模式架構的 Minimal API 的截圖

注意一點有人認為 Repository 模式有點過時 ,和過度架構 , 但我覺得還是非常有必要的,因為這樣可以更好管理你的專案。

  1. 依賴注入

依賴注入解決了應用程式如何獨立於物件建立方式的問題。當您需要可配置的應用程式單元測試時,這非常有用。隨著應用程式的規模和複雜性的增長,依賴注入可幫助您更輕鬆地更改應用程式。在 http://ASP.NET Core 中有非常好用的依賴注入方式 , 這點在 Minimal API 也是。由於我們針對功能模組去管理 ,所以在依賴注入時也可以針對模組功能去進行 。以下是我針對模組依賴注入的設計, 結合 Repository 模式以及上面提到的 ModuleExtension.cs 進行的調整 , 我也把關於資料連線公用的依賴注入也抽象了出來。

public static class ModuleExtensions
{
​
    static readonly List<IBaseModule> moduleList = new List<IBaseModule>(); 
​
    private static IEnumerable<IBaseModule> GetModules()
    {
        
        var modules = typeof(IBaseModule).Assembly
            .GetTypes()
            .Where(p => p.IsClass && p.IsAssignableTo(typeof(IBaseModule)))
            .Select(Activator.CreateInstance)
            .Cast<IBaseModule>();
​
        return modules;
    }
​
    public static void Routers(this IEndpointRouteBuilder builder)
    {
​
        foreach(var module in moduleList)
        {
            module.AddModuleRoutes(builder);
        }
​
    }
​
    public static void AddIoC(this IServiceCollection services)
    {
​
        foreach(var module in GetModules())
        {
            module.AddModuleIoC(services);
            moduleList.Add(module);
        }
​
    }
​
    public static void AddGlobalConfig(this IServiceCollection services)
    {
        services.AddScoped<CourseDataContext>();
        services.AddScoped<IUnitOfWork, UnitOfWork>();
    }
​
}

注意一些地方 , 雖然在 Minimal API 上 ,我們的依賴注入雖然把不同的Services 註冊了單例給 RequestDelegate 所呼叫 , 但在作為引數傳送時,要新增 [FromServices] 屬性標籤才有效 , 如下

    app.MapGet("/Course/GetCourse" ,([FromServices] ICourseService courseService , int typeID)=> {
            
        return  courseService.GetCourseList(typeID);
    });

Minimal API or Web API

http://ASP.NET Core 中開發 API 時,你 90% 都在使用 http://ASP.NET Core MVC。而當你使用 http://ASP.NET Core MVC 架構 Web API 時 , 你會發現有點複雜 , 你需要符合 http://ASP.NET Core MVC 的所有要求 。而 Minimal API 正好解決了這些問題 , 特別對於一些只做 API 或者 入門的開發者, 只需要簡潔的程式碼就能完成類似 Node.js 一樣的工作 。有人問我 Minimal API 會取代傳統的 Web API 嗎 ?我可以告訴大家不會。還是那句話 , 選擇符合專案需求的方法才是正道的。

小 結

在雲原生的年代 , Minimal API 是 .NET 的又一把利器 。.NET 6 的 Minimal API 要用好 ,實際上還是用到不少舊知識,像 Module 的構建方式,我參考了開源的 Carter , 像 Repository 模式還是沒有變 ,當然還是那些熟悉的語法 C# , 這就是我們常說的 ”萬變不離其中“ 。Minimal API 不是要取代 Web API , 更多是給開發人員多一個選擇 。作為 .NET 學習挑戰賽知識點的補充 , 希望能給各位小夥伴更深刻了解 Minimal API 在實際應用場景的技巧 。本次的示例程式碼也放到我的 GitHub 上了 ,如果各位小夥伴感興趣可以訪問該連結獲取完整的程式碼

相關資源

學習 Minimal API


長按識別二維碼
關注微軟中國MSDN

相關文章