ASP.NET Core + fluentValidation
从基础开始
每次写代码, 都想好好的把接口说明规范号, 不幸的是, 拖拖拉拉直到代码已经很庞大了,回过头来添加缺失的文档就是一个艰巨的任务, 嗯, 艰巨到直接忽略了😭。ASP.NET Core支持API文档有了很大的进步, 特别是在一些第三方库的帮助下, 写API文档就像写注释一样简单明了,与代码逻辑一致(很多东西都可以从代码衍生出来。
首先,通过进入项目Properties并单击Build选项卡,确保您的Web项目生成XML文档。
开箱即用,这将导致Visual Studio开始警告您项目中每个缺少的XML注释。您可以通过添加上面的1591来取消警告。
image
Swagger只是一个规范,而不是一个实现。它的正式名称是OpenAPI,但是大多数人仍将它称为Swagger。.NET的两个主要实现是Swashbuckle和NSwag。这里我使用Swashbuckle。
打开NuGet包管理器,搜索Swashbuckle.AspNetCore,并将其添加到您的项目中。
安装完成后,转到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。
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配置 - https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with- swashbuckle?view = aspnetcore-2.1&tabs = visual-studio%2Cvisual-studio-xml
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