自动绑定WebAPI中的另一个参数

Dav*_*vid 5 c# asp.net-mvc asp.net-web-api

在 MVC 中,这种事情是相当微不足道的。假设我有一个 MVC 操作签名:

public ActionResult SomeAction(InjectedObject a, ConstructedObject b)
Run Code Online (Sandbox Code Playgroud)

假设来自客户端的请求包含,并且我想在框架管道中自动ConstructedObject构建。InjectedObject(在这个例子中,InjectedObject涉及很多操作,甚至可能是所有操作。)我可以InjectedObjectModelBinder : IModelBinder在应用程序启动时创建并注册该绑定器的实例。

该活页夹将简单地构造一个InjectedObject我需要的实例。(来自请求数据、其他来源、来源组合等)这对于横切关注点非常有效。


但是,有没有办法在 WebAPI 中做到这一点?有一个新的IModelBinder,但它的使用似乎假设输入上只有一个模型。到目前为止,我的谷歌搜索也指出了这一假设。在 WebAPI 中是否有可能将某些东西作为横切关注点注入到管道中,同时仍然拥有帖子正文中构建的模型?

这里的具体用例是我想构建一个与授权相关的自定义对象,在本例中是从请求标头构建的。我可以在行动中构建它,但每个行动都需要这样做。我可以在控制器上添加扩展方法,但这会损害单元测试。首选方法是将其简单地注入到管道中,以便我可以在对控制器操作进行单元测试时注入模拟。

WebAPI 支持这个吗?或者也许还有另一种首选方法?

pet*_*ids 5

是的,您可以IModelBinder按照您建议的方式使用,只需确保模型绑定器(仅)处理您的InjectedObject. 例如,以下简单模型绑定器读取标头“your_key”:

public class InjectedObjectModelBinder : IModelBinder
{
    public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(InjectedObject))
        {
            return false;
        }

        IEnumerable<string> values;
        string keyValue = "";

        if (actionContext.Request.Headers.TryGetValues("your_key", out values))
        {
            keyValue = values.First();
        }

        bindingContext.Model = new InjectedObject() { Id = 789, Name = keyValue };

        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要将活页夹连接到类InjectedObject。有几种方法可以做到这一点:首先,您可以在每个操作方法上执行此操作:

public void Post([ModelBinder(typeof(InjectedObjectModelBinder))]InjectedObject a, ConstructedObject b)
Run Code Online (Sandbox Code Playgroud)

但这感觉不对,因为你想在很多地方使用它。InjectedObject其次,您可以通过使用属性装饰类来做到这一点ModelBinder

[ModelBinder(typeof(InjectedObjectModelBinder))]
public class InjectedObject
{....
Run Code Online (Sandbox Code Playgroud)

最后,您可以将其添加到类的方法HttpConfiguration中:RegisterWebApiConfig

var provider = new SimpleModelBinderProvider(typeof(InjectedObject), new InjectedObjectModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
Run Code Online (Sandbox Code Playgroud)

给出了上面的绑定器的一些简单ConstructedObject实现:InjectedObject

[ModelBinder(typeof(InjectedObjectModelBinder))]
public class InjectedObject
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class ConstructedObject
{
    public int A { get; set; }
    public int B { get; set; }
    public string C { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和这样的操作方法:

public void Post(InjectedObject a, ConstructedObject b)
{
    //a will be populated in our model binder.
}
Run Code Online (Sandbox Code Playgroud)

最后是来自 Fiddler 的请求,如下所示:

POST http://localhost:64577/api/values HTTP/1.1
Host: localhost:64577
Accept: */*
Content-Type: application/json
Connection: keep-alive
Content-Length: 51
your_key: This came from the header

{"a":1,"b":2,"c":"This was 'normal' model binding"}
Run Code Online (Sandbox Code Playgroud)

绑定的行为正如您所期望/希望的那样:

具有正确绑定值的观察窗口

可以在此处找到一篇有关 Web API 中的模型绑定的好文章。