自动绑定Web API中的另一个参数

时间:2015-04-07 12:40:18

标签: c# asp.net-mvc asp.net-web-api

在MVC中,这种事情非常简单。让我们说我有一个MVC动作签名:

public ActionResult SomeAction(InjectedObject a, ConstructedObject b)

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

该绑定器只是构造一个InjectedObject的实例,但我需要这样做。 (来自请求数据,来自其他来源,来源的组合等)这非常适合跨领域的关注。


但是,有没有办法在WebAPI中执行此操作?有一个新的IModelBinder,但似乎它的用法假设输入上只有一个模型。到目前为止,我的谷歌搜索也指出了这一假设。是否有可能在WebAPI中将某些内容注入到管道中,这是一个跨领域的问题,同时仍然从帖体中构建模型?

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

WebAPI是否支持此功能?或许还有另一种首选方法?

1 个答案:

答案 0 :(得分:4)

是的,您可以按照建议的方式使用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;
    }
}

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

public void Post([ModelBinder(typeof(InjectedObjectModelBinder))]InjectedObject a, ConstructedObject b)

但考虑到你想在很多地方使用它,这感觉不对。其次,您可以通过使用InjectedObject属性装饰ModelBinder类来完成此操作:

[ModelBinder(typeof(InjectedObjectModelBinder))]
public class InjectedObject
{....

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

var provider = new SimpleModelBinderProvider(typeof(InjectedObject), new InjectedObjectModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);

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

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

和这样的动作方法:

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

最后来自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"}

绑定的行为与您期望的一样/希望:

Watch window with correctly bound values

可以找到关于Web API中模型绑定的好文章here