ASP.NET Core + fluentValidation + Swagger
從基礎開始
每次寫程式碼, 都想好好的把介面說明規範號, 不幸的是, 拖拖拉拉直到程式碼已經很龐大了,回過頭來新增缺失的文件就是一個艱鉅的任務, 嗯, 艱鉅到直接忽略了:sob:。ASP.NET Core支援API文件有了很大的進步, 特別是在一些第三方庫的幫助下, 寫API文件就像寫註釋一樣簡單明瞭,與程式碼邏輯一致(很多東西都可以從程式碼衍生出來。
首先,通過進入專案 Properties 並單擊 Build 選項卡,確保您的Web專案生成XML文件。

image
開箱即用,這將導致Visual Studio開始警告您專案中每個缺少的XML註釋。您可以通過新增上面的1591來取消警告。

image
Swagger 只是一個規範,而不是一個實現。它的正式名稱是OpenAPI,但是大多數人仍將它稱為Swagger。.NET的兩個主要實現是 Swashbuckle 和 NSwag 。這裡我使用Swashbuckle。
開啟NuGet包管理器,搜尋Swashbuckle.AspNetCore,並將其新增到您的專案中。

image
安裝完成後,轉到 Startup.cs 檔案並新增幾行程式碼。在 ConfigureServices中 ,提供以下程式碼:
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info() { Title = "Web App", Version = "v1" }); // Set the comments path for the Swagger JSON and UI. var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); });
然後,在Configure中,新增以下程式碼行:
app.UseSwagger(); if (env.IsDevelopment()) { app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Web App V1"); }); }
此時,您可以除錯您的應用程式。預設情況下,swashbuckle會為您的應用程式新增一個路徑,因此如果您導航到 / swagger ,將顯示SwaggerUI。

image
using Microsoft.AspNetCore.Mvc; namespace WebApplication.ApiControllers { /// <summary> /// Verifies that the swagger documentation generator works as expected. /// </summary> [Route("api/[controller]")] [ApiController] public class TestController : ControllerBase { /// <summary> /// Retrieves test data. /// </summary> /// <returns>The test data.</returns> [HttpGet] public IActionResult GetTestData() { var model = new { Message = "Hello, world!" }; return Ok(model); } } }
再次啟動SwaggerUI,我們可以看到列出了新的API端點。

image
請注意, <summary> 註釋顯示在路徑旁邊。我們可以使用 [SwaggerResponse] 屬性提供有關不同響應的其他資訊。
/// <summary> /// Retrieves test data. /// </summary> /// <returns>The test data.</returns> [HttpGet] [SwaggerResponse((int)HttpStatusCode.OK, Description = "The data was returned successfully.")] [SwaggerResponse((int)HttpStatusCode.Unauthorized, Description = "You are not authorized to access this resource.")] public IActionResult GetTestData() { var model = new { Message = "Hello, world!" }; return Ok(model); }
這將更新swagger文件:

image
我特別喜歡您可以將不同的返回型別關聯到每個響應,因此您可以指定成功時返回正常模型但出錯時返回錯誤模型。
注意:在撰寫本文時,您必須指定方法(例如, [HttpGet] ),否則SwaggerUI將返回錯誤。
模型驗證
Razor(MVC)集成了許多圍繞驗證的功能,以 ModelState 和資料註釋為中心。隨著SPA和WebAPI的引入,我完全停止使用ModelState,並回到在我的API呼叫頂部編寫顯式if語句進行驗證。
ModelState通過在 System.ComponentModel.DataAnnotations 名稱空間中定義API模型上的資料註釋來工作。這些註釋涵蓋了最常見的驗證形式:必填?有最小長度?有最大長度?有效電郵?匹配正則表示式?在真正投入生產的網站,驗證可能與業務邏輯本身一樣複雜,嗯, 有時候更復雜, 你無法知道到底是什麼樣的人在使用你的系統。
特性在編譯時就是確定的,這意味著您無法輕鬆執行復雜邏輯。例如,您必須實現自己的驗證屬性(或從 IValidatableObject 繼承)才能從配置檔案或資料庫中讀取。
現在,讓我們考慮一個使用資料註釋和 IValidatableObject 介面的模型:
public class AddressModel : IValidatableObject { [Required] [MaxLength(100)] public string Line1 { get; set; } [MaxLength(100)] public string Line2 { get; set; } [Required] [MaxLength(100)] public string City { get; set; } [Required] [MaxLength(2)] public string State { get; set; } [Required] [RegularExpression(@"^\d{5}(-?\d{4})?$")] public string Zip { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { var stateRepository = (IStateRepository)validationContext.GetService(typeof(IStateRepository)); var state = stateRepository.GetState(State); if (state == null) { yield return new ValidationResult("Unknown state", new[] { nameof(State) }); } } }
以下是該模型的swagger文件:

image
我們的 AddressModel 當然不再是一個簡單的POCO,從DI容器中獲取 IStateRepository 很醜陋。另外,隨著API傾向於非同步操作,這種同步驗證變得不可接受。那麼,我們是否會回到一英里長的if語句?
FluentValidation為勝利!
如果你像我一樣,當Entity Framework 5引入了流暢的配置時,你會非常放心。開發人員可以使用鏈式方法呼叫來定義配置,而不是使用討厭的EDMX檔案或更多屬性。 FluentValidation 是一個庫,它實現了相同的流暢配置鏈,但用於驗證。
以下是使用FluentValidation 對 AddressModel 進行的等效驗證:
public class AddressModelValidator : AbstractValidator<AddressModel> { private readonly IServiceProvider serviceProvider; public AddressModelValidator(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; RuleFor(x => x.Line1).NotEmpty(); RuleFor(x => x.Line1).MaximumLength(100); RuleFor(x => x.Line2).MaximumLength(100); RuleFor(x => x.City).NotEmpty(); RuleFor(x => x.City).MaximumLength(100); RuleFor(x => x.State).NotEmpty(); RuleFor(x => x.State).MaximumLength(2); RuleFor(x => x.Zip).NotEmpty(); RuleFor(x => x.Zip).Matches(@"^\d{5}(-?\d{4})?$"); RuleFor(x => x.State).MustAsync(IsKnownState).When(x => x.State != null); } private async Task<bool> IsKnownState(string abbreviation, CancellationToken token) { var stateRepository = serviceProvider.GetRequiredService<IStateRepository>(); var state = await stateRepository.GetStateAsync(abbreviation, token); return state != null; } }
注意我能夠輕鬆地將 IServiceProvider 注入到建構函式中,這樣,我們可以輕鬆地按需檢索依賴項。還可以使用 MustAsync 提供非同步實現來檢查提供狀態的有效性。
另請注意,我沒有直接將IStateRepository介面注入建構函式(也許這樣更好?)。這是生命週期範圍由依賴注入框架管理的方式的工件。您還會注意到我使用了 GetRequiredServices ,這是一種擴充套件方法,可以更輕鬆地使用 IServiceProvider 介面。
為了讓ASP.NET Core知道使用FluentValidation,我們必須更新 Startup.cs 再次歸檔。首先,開啟NuGet包管理器並將 FluentValidation.AspNetCore 新增到您的專案中。在 ConfigureServices 方法中, 將對AddFluentValidation 的呼叫標記到 AddMvc 方法上。
services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddFluentValidation(o => { o.RegisterValidatorsFromAssemblyContaining<AddressModelValidator>(); });
好訊息是FluentValidation將重用ASP.NET Core提供的依賴注入配置。對 RegisterValidatorsFromAssemblyContaining 的呼叫會自動將程式集中的所有驗證程式類註冊到服務集合中。這就是允許我將 IServiceProvider 或 IValidatorFactory 等東西 傳遞 給建構函式的原因。該 AddFluentValidation 方法提供了配置FluentValidation與ASP.NET核心工作的幾種方法。
使用Swagger註冊FluentValidation
如果再次開啟SwaggerUI,您會注意到我們丟失了所有模型驗證資訊。預設情況下,Swashbuckle只知道如何生成資料註釋的文件。

image
幸運的是,其他一些聰明人已經經歷了整合Swashbuckle和FluentValidation的痛苦。只需新增MicroElements.Swashbuckle.FluentValidation NuGet包,現在您可以對AddSwaggerGen呼叫進行簡單修改:
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info() { Title = "Web App", Version = "v1" }); // Set the comments path for the Swagger JSON and UI. var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); c.AddFluentValidationRules(); });
這一行程式碼允許swagger檢查您的驗證器類,以構建資料註釋附帶的等效文件。所以現在,當你開啟SwaggerUI時,你可以檢視原來的相同級別的詳細資訊:

image
正如我所示,向ASP.NET Core新增API文件非常簡單。到目前為止,我喜歡ASP.NET Core的靈活性。
參考
FluentValidation文件 - https://github.com/JeremySkinner/FluentValidation/wiki/a.-Index
Swagger / FluentValidation Integration - https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation
自定義Swagger文件 - https:// www。 schaeflein.net/adding-implementation-notes-to-swagger-ui-via-swashbuckle-attributes/
ASP.NET模型驗證 - https://docs.microsoft.com/en-us/aspnet/core/mvc/models/驗證?檢視= aspnetcore-2.1