在 ASP.NET Core Web API 中,我可能有一个端点,如下所示:
[HttpPost("doSomething")]
public async Task<IActionResult> DoSomething([FromBody] SomeObject request)
{
// ...
}
对于这样的物体:
public class SomeObject
{
public string Subject { get; set; }
public string Body { get; set; }
public int Priority { get; set; }
}
在调用上述端点时,我必须在发布请求中提供主题、正文和优先级,否则我将遇到异常。这是一件好事,因为我知道客户端已经提供了这些值(无论它们是否是默认值,null 或空格都不重要 - 我只需要确保客户端已经提供了它们,Web API 会为我检查这一点).
现在使用最少的 API,我尝试使用以下代码复制上述行为:
app.MapPost("/doSomething", async ([FromBody] SomeObject request) =>
{
// ...
});
问题?虽然我可以访问 request 的值,但不能保证客户端已提供该对象的所有值。如果没有,则不会抛出异常。我现在必须对每个字段进行手动检查。
我想知道是否有一个简单的解决方案
[FromBody]
类型的所有必需字段(我猜是通过反射)?也许是这样的:
app.MapPost<T>("/doSomething", async ([FromBody] T request) =>
{
// ...
});
我可以将该端点的特定类型代替
T
(并不是说这将如何完成,而只是我所追求的通用解决方案类型的一个示例)。
所以本质上我的问题是:如何在最小 API 中复制 Web API 的
[FromBody]
行为,特别是在调用端点的客户端未提供对象的所有必需字段时与抛出异常相关?
Fluent Validation
库来验证您传入的请求。
例如:
public record SomeObject
{
public string Subject { get; init; }
public string Body { get; init; }
}
public class SomeObjectValidator : AbstractValidator<SomeObject>
{
private const int MaxSubjectLength = 250;
private const int MaxBodyLength = 3000;
public SomeObjectValidator()
{
RuleFor(_ => _.Subject)
.NotEmpty()
.MaximumLength(MaxSubjectLength)
.WithMessage($"The subject must be no longer than {MaxSubjectLength} characters");
RuleFor(_ => _.Body)
.NotEmpty()
.MaximumLength(MaxBodyLength)
.WithMessage($"Body cannot exceed {MaxBodyLength} characters");
}
}
app.MapPost("/dosomething", async ([FromBody] SomeObject request, IValidator<SomeObject> validator) =>
{
var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return Results.ValidationProblem(validationResult.ToDictionary());
}
// continue with rest of your logic
});
要将其连接在一起,您需要向 IoC 容器注册您的验证。
builder
.Services
.AddScoped<IValidator<SomeObject>, SomeObjectValidator>();