是否有一种方法可以在目标模型之外以流畅的方式定义绑定属性(FromBody
、FromQuery
等)?类似于FluentValidation与[Required]
、[MaxLength]
等属性。
背景故事:
我想使用命令模型作为控制器动作参数:
[HttpPost]
public async Task<ActionResult<int>> Create(UpdateTodoListCommand command)
{
return await Mediator.Send(command);
}
更重要的是,我希望模型能够从多个源(路径、主体等)绑定:
[HttpPut("{id}")]
public async Task<ActionResult> Update(UpdateTodoListCommand command)
{
// command.Id is bound from the route, the rest is from the request body
}
这应该是可能的(https://josef.codes/model-bind-multiple-sources-to-a-single-class-in-asp-net-core/,https://github.com/ardalis/RouteAndBodyModelBinding),但是需要在命令的属性上绑定属性,这应该避免。
1条答案
按热度按时间aurhwmvo1#
可以使用自定义IModelBinderProvider自定义AspNetCore modelBinding进程而不使用属性。
我将解释一种方法来实现如下的请求结果:
预期响应正文:
因此,控制器应该将来自路由、查询和主体的所有数据混合为一个模型,然后返回序列化的模型(我们只想检查示例中的自定义绑定结果)。
C# POCO可以是:
控制器:
我们需要一些样板代码来声明关于命令的元数据,并定义哪个属性应该绑定到查询或路由数据。我把它做得非常简单,因为这不是主题的目的:
该存储将包含要与自定义进程绑定的所有命令的元数据快照。
商店的流畅构建器可以像下面这样(我再一次尝试简单化):
现在我们可以创建一个
IModelBinderProvider
的自定义实现。这个实现负责为我们商店的每个命令向MVC给予一个自定义IModelBinder
。我们的命令是复杂类型的,所以我们必须获得一些元数据(从MVC API)来简化属性的绑定:自定义绑定器将读取请求主体(在示例中为JSON,但您可以按任何需要的格式读取和解析):
如果我们现在执行代码(假设MVC知道自定义提供程序,但在我的示例的这个阶段,这不是真的),只有属性Description会按预期绑定。因此,我们必须从QueryString绑定属性。在MVC绑定理念中,ValueProviders负责从请求中获取原始值:
QueryStringValueProvider
是QueryString的一个,所以我们可以使用它:这里很容易使用反射来设置我们命令的属性,但是MVC给予了我们一些工具,所以我认为使用它们更好。而且,我们只是得到一个类型为
StringValues
的原始值。因此将其转换为预期的属性类型可能会很麻烦(考虑UpdateTodoListCommand
的属性Index
)。现在可以使用在自定义IModelProvider中创建的绑定:现在,在我们的示例中,Title和Index将按预期绑定。Id属性可以与
RouteValueProvider
绑定:最后要做的是告诉MVC关于我们自定义的
IModelBinderProvider
,应该在MvcOptions
中完成:这里有一个完整的要点:https://gist.github.com/thomasouvre/e5438816af1a0ad81bddf106432cfa7d
编辑:当然,您可以使用自定义
IOperationProcessor
来自定义NSwag操作生成,如下所示:Swagger UI的实际结果: