Ary*_*dlé 4 c# asp.net-core-mvc asp.net-core
我拥有的
我有一个具有以下方法的 api 控制器(ASP.NET Core MVC):
[HttpPost]
[Route("delete")]
public Task<ActionResult> SomeAction(Guid[] ids, UserToken userToken, CancellationToken cancellationToken)
{
....
}
Run Code Online (Sandbox Code Playgroud)
我有一个自定义模型绑定器和绑定器提供程序:
public class UserTokenBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(UserToken))
{
return new BinderTypeModelBinder(typeof(UserTokenBinder));
}
return null;
}
}
public class UserTokenBinder: IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var token = await bindingContext.ActionContext.HttpContext.User.ToUserTokenAsync(CancellationToken.None);
bindingContext.Result = ModelBindingResult.Success(token ?? UserToken.UnidentifiedUser);
}
}
Run Code Online (Sandbox Code Playgroud)
将活页夹提供程序添加到服务:
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new UserTokenBinderProvider());
});
Run Code Online (Sandbox Code Playgroud)
问题
当服务器正在加载时,我收到以下异常 ( InvalidOperationException):
... 'SomeAction' 具有多个指定或推断为从请求正文绑定的参数。每个动作只能从主体绑定一个参数。检查以下参数,并使用“FromQueryAttribute”指定查询绑定,“FromRouteAttribute”指定路由绑定,“FromBodyAttribute”指定要从正文绑定的参数: Guid[] ids, UserToken userToken
MVC 似乎忽略了我对该UserToken类型的自定义绑定器,并尝试使用默认方法绑定它。任何想法为什么?
编辑 在这里收到答案后,打开了一个问题来修改 ASP.NET Core 文档。
该[ApiController]属性的存在为动作参数引入了绑定源参数推断。在启动时,动作模型约定针对所有检测到的控制器动作运行并推断绑定源。对于复杂类型,例如您的Guid[]和UserToken参数,此推理选择请求正文作为源 - 就好像您自己添加[FromBody]了这两个参数一样,如下所示:
public Task<ActionResult> SomeAction(
[FromBody] Guid[] ids,
[FromBody] UserToken userToken,
CancellationToken cancellationToken)
Run Code Online (Sandbox Code Playgroud)
在你的问题中,你说:
MVC 似乎忽略了我为 UserToken 类型设置的自定义绑定器,并尝试使用默认方法绑定它。
这不是这里发生的事情。它还没有尝试绑定任何东西——它只是尝试在启动时配置绑定源,甚至在模型绑定发生之前。您已经正确地指示 MVC 使用您的自定义模型绑定器,但是我上面提到的操作模型约定对IModelBinderProvider您添加的内容一无所知。即使这样做了,模型绑定器提供者和类型 ( UserToken)之间的实际关联在GetBinder方法运行之前也是未知的,这仅在需要模型绑定时发生;在配置应用程序模型时不在启动时。
如果你要更新你的UserToken类以包含一个[ModelBinder]属性,它会一切正常(你甚至可以删除UserTokenBinderProvider):
[ModelBinder(typeof(UserTokenBinderProvider))]
public class UserToken { }
Run Code Online (Sandbox Code Playgroud)
这种方法的最大缺点是您的UserToken类将依赖于 MVC 属性,这可能不是您想要的。那么,还有更好的吗?
现在,您可能想知道为什么我没有显示上面[FromBody]的CancellationToken参数。这是否意味着CancellationToken得到特殊待遇?是的,确实如此。将 ABindingSourceMetadataProvider添加到将MvcOptions其绑定源指定为 的实例BindingSource.Special。当动作模型约定运行并尝试推断绑定源时,它会看到绑定源已经设置并不管它。
要解决您的问题,请BindingSourceMetadataProvider为您的UserToken类型添加一个并使用BindingSource.Special,如下所示:
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new UserTokenBinderProvider());
options.ModelMetadataDetailsProviders.Add(
new BindingSourceMetadataProvider(typeof(UserToken), BindingSource.Special));
});
Run Code Online (Sandbox Code Playgroud)