具有多个参数的webapi方法的自定义模型绑定器 [英] Custom model binder for a webapi method with more than one parameter

查看:178
本文介绍了具有多个参数的webapi方法的自定义模型绑定器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我所拥有的

我有一个采用以下方法的api控制器(ASP.NET Core MVC):

I have an api controller (ASP.NET Core MVC) with the following method:

[HttpPost]
[Route("delete")]
public Task<ActionResult> SomeAction(Guid[] ids,  UserToken userToken, CancellationToken cancellationToken)
{
   ....
}

我有一个自定义模型活页夹和活页夹提供程序:

I have a custom model binder and binder provider:

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);
    }
}

将绑定提供程序添加到服务中:

Added the binder provider to services:

services.AddMvc(options =>
{                    
    options.ModelBinderProviders.Insert(0, new UserTokenBinderProvider());
});

问题

服务器正在加载时,出现以下异常(InvalidOperationException):

While server is loading I'm getting the following exception (InvalidOperationException):

...'SomeAction'具有多个参数,这些参数是从请求正文中指定或推断为绑定的.每个动作只能绑定一个参数.检查以下参数,并使用'FromQueryAttribute'指定来自查询的绑定,'FromRouteAttribute'指定来自路由的绑定,以及'FromBodyAttribute'指定从body绑定的参数: Guid [] ID, UserToken userToken

... 'SomeAction' has more than one parameter that was specified or inferred as bound from request body. Only one parameter per action may be bound from body. Inspect the following parameters, and use 'FromQueryAttribute' to specify bound from query, 'FromRouteAttribute' to specify bound from route, and 'FromBodyAttribute' for parameters to be bound from body: Guid[] ids, UserToken userToken

似乎MVC忽略了我为UserToken类型使用的自定义绑定程序,并尝试使用默认方法对其进行绑定. 有什么想法吗?

It seems that MVC disregards the custom binder I have for the UserToken type and tries to bind it using default methods. Any ideas why?

编辑 在这里收到答案后,打开了问题来修改ASP.NET Core文档.

EDIT After receiving an answer here, an issue was opened to amend ASP.NET Core documentation.

推荐答案

[ApiController]属性的存在会引入

The presence of the [ApiController] attribute introduces Binding source parameter inference for action parameters. At startup, an action model convention runs against all detected controller actions and infers the binding sources. For complex types, such as your Guid[] and UserToken parameters, this inference chooses the request body as the source - it's as though you'd added [FromBody] to both of those parameters yourself, like this:

public Task<ActionResult> SomeAction(
    [FromBody] Guid[] ids,
    [FromBody] UserToken userToken,
    CancellationToken cancellationToken)

在您的问题中,您声明:

In your question, you state:

MVC似乎无视我为UserToken类型设置的自定义绑定程序,并尝试使用默认方法对其进行绑定.

It seems that MVC disregards the custom binder I have for the UserToken type and tries to bind it using default methods.

这不是这里正在发生的事情.它还没有试图绑定任何东西-它只是在启动模型之前甚至在绑定发生之前就配置了绑定源.您已经正确地指示MVC使用自定义模型绑定程序,但是我上面提到的动作模型约定对您添加的IModelBinderProvider一无所知.即使这样,在GetBinder方法运行之前,也不知道模型绑定程序提供程序和类型(UserToken)之间的实际关联,只有在需要模型绑定时才会发生这种情况.在配置应用程序模型时不会在启动时出现.

This isn't quite what's going on here. It's not trying to bind anything yet - it's just trying to configure the binding sources at startup, before model binding can even occur. You've correctly instructed MVC to use your custom model binder, but the action model convention I mentioned above doesn't know anything about the IModelBinderProvider you've added. Even if it did, the actual association between the model binder provider and the type (UserToken) isn't known until the GetBinder method runs, which only happens when model binding is needed; not at startup when the application model is being configured.

如果要更新UserToken类以使其包含[ModelBinder]属性,则所有功能都可以使用(甚至可以删除UserTokenBinderProvider):

If you were to update your UserToken class to include a [ModelBinder] attribute, it would all work (you could even remove UserTokenBinderProvider):

[ModelBinder(typeof(UserTokenBinderProvider))]
public class UserToken { }

这种方法的最大缺点是您的UserToken类将依赖于MVC属性,而这可能并不是您想要的.那么,有没有更好的东西?

The big downside to this approach is that your UserToken class would be dependent on an MVC attribute, which might not be something you want. So, is there something better?

现在,您可能想知道为什么我没有在上面的CancellationToken参数中显示[FromBody].这是否意味着CancellationToken得到特殊待遇? 是的, .将BindingSourceMetadataProvider添加到MvcOptions实例,该实例将其绑定源指定为

Now, you might be wondering why I didn't show [FromBody] for the CancellationToken parameter above. Does this mean that CancellationToken gets special treatment? Yes, it does. A BindingSourceMetadataProvider is added to the MvcOptions instance that specifies its binding source as BindingSource.Special. When the action model convention runs and attempts to infer the binding source, it sees that the binding source is already set and leaves it alone.

要解决您的问题,请为UserToken类型添加BindingSourceMetadataProvider并使用BindingSource.Special,如下所示:

To resolve your issue, add a BindingSourceMetadataProvider for your UserToken type and use BindingSource.Special, like this:

services.AddMvc(options =>
{                    
    options.ModelBinderProviders.Insert(0, new UserTokenBinderProvider());
    options.ModelMetadataDetailsProviders.Add(
        new BindingSourceMetadataProvider(typeof(UserToken), BindingSource.Special));
});

这篇关于具有多个参数的webapi方法的自定义模型绑定器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆