在我們開發專案的Web API的時候,隨著專案功能要求越來越多,可能我們會為控制器基礎類別增加越來越多的基礎功能,有些功能有一定的適應性,但可能在一般的子類中用不到,而隨著對控制器控制要求越來越精細,那麼需要為基礎類別或者子類增加更多的控制功能,這樣隨著迭代的進行,有些控制器的功能會顯得越來越笨重。這個時候,一種更加靈活、輕便的Web API處理方式,對每個控制器方法的垂直切割的API框架應運而生,本篇隨筆介紹的FastEndpoints 就是其中這樣的一款框架,本篇隨筆介紹一些FastEndpoints的基礎處理方法,並通過一些基礎的案例,把我們《 SqlSugar 開發框架》的一些模組進行遷移性測試,對比相關後端Web API的處理,一起分享給大家。
FastEndpoints 是Minimal API和MVC的開發人員友好替代品,它是基於REPR設計模式(請求-端點-響應),以便建立方便且可維護的端點,幾乎沒有樣板檔案。
FastEndpoints 的效能與Minimal API 相當,甚至它更快,使用更少的記憶體並且每秒請求數比基準測試中的MVC控制器更高。對於比如:中介軟體、認證、授權、紀錄檔,依賴注入這些常用功能都支援,甚至有些還進行了加強。
設計主要是分為兩種模式
分層模式:mvc、mvp、mvvm等
垂直模式:REPR設計模式
REPR設計模式就是垂直模式,系統的每個元件都是單獨的一塊,彼此並不影響,就像微服務那樣。
MVC - 模型-檢視-控制器旨在與使用者介面配合使用。顯然,檢視是一個 UI 元件。如果您正在構建 API,則沒有檢視,因此您充其量使用的是 MC 模式,或者您可以將其稱為模型-操作-控制器並獲取 MAC 模式。關鍵是,你已經沒有將MVC用於你的API,所以考慮一個更合適的模式應該不是一個很大的問題。
API 端點是非常獨立的,每個端點都可以使用三個元件來描述:
請求(Request):終結點所需的資料形狀
終結點(Endpoint):終結點在給定請求時執行的邏輯
響應(Response):終結點返回給呼叫方的響應
結合這三個元素,你會得到請求-端點-響應或 REPR 模式。
並非所有終結點都需要其請求或響應的實際資料,在某些情況下,不接收任何輸入或僅返回 HTTP 狀態程式碼。但是,在此模式中,空請求或響應仍然是有效的請求或響應,就像某些 MVC 操作不需要模型一樣。
使用 API 端點庫時,您可以將請求、終端節點和響應型別分組在一起,這樣就無需在某些「檢視模型」或「dtos」資料夾中四處尋找合適的型別。它減少了摩擦,使使用單個端點變得更加容易。
FastEndPoint GitHub庫:https://github.com/FastEndpoints/FastEndpoints
FastEndpoints 線上檔案:https://fast-endpoints.com
參考官方的檔案介紹,我們可以很容易的建立出一個簡單的類似Hello開篇的API應用。
我建立一個基於.net core的Web API專案,先把FastEndPoint的相關參照加入專案中,如下所示。
然後在專案中的啟動類程式碼中,我們新增相關的程式碼使用FastEndpoints,如下所示。
using FastEndpoints; using FastEndpoints.Swagger; var bld = WebApplication.CreateBuilder(); bld.Services .AddFastEndpoints() .SwaggerDocument(); var app = bld.Build(); app.UseFastEndpoints() .UseSwaggerGen(); app.Run();
如果需要對Swagger進行一些客製化修改,可以改動如下,這裡先忽略。
.SwaggerDocument(o => { o.DocumentSettings = s => { s.Title = "SqlSugar框架介面API檔案"; s.Version = "v1"; }; o.TagDescriptions = t => { t["Test"] = "測試介面"; t["User"] = "使用者相關介面"; }; })
為了簡便,我們以命名控制元件不同,以及目錄,來區分不同的Web API分組,如下所示,我們建立一個基於Test的相關API介面。
對於以前的控制器介面來說,一般可能一個控制(如TestController)會包含多個方法,如上面的Create、List方法,這裡使用的是FastEndpoints,它們是把一個大型的控制器切換為一個方法一個類來處理,碎片化意味著類的增加,不過我們不需要做太多的工作,可以通過它們的一些基礎類別來簡化這個過程。
我們把WebAPI中請求的Request和Response的物件,放在一個Model類檔案裡面,如下程式碼所示。
namespace FastWebApi.Controllers.Test { /// <summary> /// 測試請求資訊 /// </summary> public class TestRequest { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } /// <summary> /// 測試返回資訊 /// </summary> public class TestResponse { public string FullName { get; set; } public bool IsOver18 { get; set; } } }
我們來看看基於FastEndPoints方式 生成一個Create的請求Web API方法,如下程式碼所示
namespace FastWebApi.Controllers.Test { //客戶使用標識,不用覆蓋 Configure 函數 //[HttpPost("/api/user/create")] //[AllowAnonymous] /// <summary> /// 建立記錄 /// </summary> public class Create : Endpoint<TestRequest, AjaxResponse> { public override void Configure() { Post("/test/create"); AllowAnonymous(); } public override async Task HandleAsync(TestRequest req, CancellationToken ct) { var result = new TestResponse() { FullName = req.FirstName + " " + req.LastName, IsOver18 = req.Age > 18 }; await SendAsync(result.ToAjaxResponse()); } } }
我們設定Web API方法的路由,可以通過在Configure函數中指定: Post("/test/create")
也可以通過Attribute屬性標識的方式,來宣告,上面的註釋程式碼所示。
[HttpPost("/api/user/create")]
這兩者是等同的,任何一種方式都可以,預設的介面是需要授權才能存取的,如果我們標識了
[AllowAnonymous]
就可以匿名存取Web API 的方法了,Web API的方法處理邏輯,都是統一通過重寫 HandleAsync 方法進行實現的,如上面程式碼所示。
其中AjaxResponse 是我定義的一個統一返回結果,這樣我們的介面模型就一致了。
如下是Web API統一封裝後返回的結果物件。
如果需要了解我的《SqlSugar開發框架》的統一結果返回處理,可以參考《基於SqlSugar的資料庫存取處理的封裝,在.net6框架的Web API上開發應用 》中的 【統一結果封裝和例外處理】 部分內容即可。
如果不需要統一返回模型,則可以自定義為任何的返回型別,如下是官方的案例所示。
public class MyEndpoint : Endpoint<MyRequest, MyResponse> { public override void Configure() { Post("/api/user/create"); AllowAnonymous(); } public override async Task HandleAsync(MyRequest req, CancellationToken ct) { await SendAsync(new() { FullName = req.FirstName + " " + req.LastName, IsOver18 = req.Age > 18 }); } }
接下來,我們檢查下.netcore專案的launchSettings.json 設定資訊
確保開啟的時候就啟動Swagger頁面即可。
啟動Swagger頁面,我們來看看具體的效果,可以看到有兩個Test的介面,如下所示。
我們來偵錯Swagger,並測試下結果返回。
測試返回的結果如下所示,由於採用了統一返回結果的處理,這裡返回的TestResponse的物件序列化資訊,放在了result的裡面了,如下所示。
而List的控制器方法,這裡沒有請求輸入的物件資訊,因此引數為空。具體的API方法定義如下所示。
namespace FastWebApi.Controllers.Test { /// <summary> /// 獲取所有記錄 /// </summary> [HttpGet("/test/list")] [AllowAnonymous] public class List : EndpointWithoutRequest<AjaxResponse> { /// <summary> /// 處理返回 /// </summary> public override async Task HandleAsync(CancellationToken ct) { var result = new List<TestResponse>() { new TestResponse { FullName= "test", IsOver18 = true, }, new TestResponse { FullName= "test 2", IsOver18 = false, } }; await SendAsync(result.ToAjaxResponse()); } } }
Swagger介面展示介面效果。
正常執行返回結果如下所示。
如果處理過程中有異常,由於我們採用了統一返回結果處理,因此異常資訊也需要統一在物件裡面,返回結果如下所示。
以上就是簡單型別的一些處理例子,結合了統一返回結果的處理,我們可以很好的定義一個通用的結果返回。
上面我們為了更好理解FastEndpoints的碎片化介面的處理,我們做了兩個簡單的方法來測試。
下面我們通過對我們SqlSugar開發框架中的基礎類別介面進行功能上的拆分,並結合實際業務的需要介面,進行擴充套件的處理,從而也實現了常規CRUD的操作介面,並實現特殊業務類的API介面處理。
關於Web API的常規介面處理 ,我們為了簡化程式碼,往往抽象一些常規的CRUD方法在控制器基礎類別中,這樣可以極大的減少了繼承子類的介面程式碼,通過繼承基礎類別,子類自動具備了CRUD的處理介面,只需要根據業務的需要,增加一些特殊的業務介面即可。
以前的處理方法,我們是根據專案的需要,我們定義了一些控制器的基礎類別,用於實現不同的功能,如下介面所示。
其中ControllerBase是.net core Web API中的標準控制器基礎類別,我們由此派生一個LoginController用於登入授權,而BaseApiController則處理常規介面使用者身份資訊,而BusinessController則是對標準的增刪改查等基礎介面進行的封裝,我們實際開發的時候,只需要開發編寫類似CustomerController基礎類別即可。
而現在採用FastEndpoints ,需要垂直切割整個控制器,我們需要把基礎類別的介面實現放到具體的業務API類裡面,我們為了方便,可以給他們不同的名稱一個介面,或者組合在一個檔案裡面,如下所示。
我們來看看其中給一個簡單的Count方法介面實現。
namespace FastWebApi.Controllers.User { /// <summary> /// 根據條件計算記錄數量 /// </summary> [HttpGet("/user/count")] public class Count : Endpoint<UserPagedDto, AjaxResponse> { /// <summary> /// 處理請求 /// </summary> public override async Task HandleAsync(UserPagedDto req, CancellationToken ct) { var result = await BLLFactory<IUserService>.Instance.CountAsync(req); await SendAsync(result.ToAjaxResponse()); } } }
這裡可以採用介面注入的方式,也可以採用我們輔助類BLLFactory<IUserService>.Instance方法呼叫介面,一樣的實現。
這樣結合了業務的具體Service來處理,只需要簡單的處理下即可,也算比較方便,由於這些基礎的CRUD的方法,主要路由、分頁物件,業務物件,主鍵型別的不同,這些可以通過我們的程式碼生成工具的處理快速生成即可,因此可以實現批次化的業務類的API介面方法生成。
至於具體的業務介面API,我們就需要手工處理了,如對於使用者的登陸獲取token的方法,我們這裡需要模仿來生成一個EndPiont類,如下所示。
/// <summary> /// 根據使用者名稱、密碼驗證使用者身份有效性 /// </summary> [HttpPost("/user/verify-user")] [AllowAnonymous] public class VerifyUser : Endpoint<VerifyUserDto, AjaxResponse> { /// <summary> /// 處理請求 /// </summary> public override async Task HandleAsync(VerifyUserDto input, CancellationToken ct) { var result = await BLLFactory<IUserService>.Instance.VerifyUser(input.UserName, input.UserPassword, input.SystemType, input.IP, input.MacAddr); await SendAsync(result.ToAjaxResponse()); } }
其他業務方法也是類似的處理,這裡的FastEndPoints的處理類,只是增加了一個簡單的包裝層就可以了,最後看看這些方法在SwaggerUI中的展示,和我們普通模式的Web API中的Swagger UI介面類似的效果。
這樣,我們可以在保持介面一致性的情況下,無縫的對接新的Web API介面後端了。