在MVC中,这种事情非常简单。让我们说我有一个MVC动作签名:
public ActionResult SomeAction(InjectedObject a, ConstructedObject b)
让我们说来自客户端的请求包含ConstructedObject
,我想自动在框架管道中构建InjectedObject
。 (在此示例中,InjectedObject
涉及许多操作,甚至可能是所有操作。)我可以创建一个InjectedObjectModelBinder : IModelBinder
并在应用程序启动时注册该绑定器的实例。
该绑定器只是构造一个InjectedObject
的实例,但我需要这样做。 (来自请求数据,来自其他来源,来源的组合等)这非常适合跨领域的关注。
但是,有没有办法在WebAPI中执行此操作?有一个新的IModelBinder
,但似乎它的用法假设输入上只有一个模型。到目前为止,我的谷歌搜索也指出了这一假设。是否有可能在WebAPI中将某些内容注入到管道中,这是一个跨领域的问题,同时仍然从帖体中构建模型?
这里的具体用例是我想构建一个自定义授权相关对象,在本例中是来自请求标头。我可以在动作中构建它,但每个动作都需要。我可以在控制器上添加扩展方法,但这会损害单元测试。首选的方法是简单地将它注入管道,这样我就可以在单元测试控制器动作时注入模拟。
WebAPI是否支持此功能?或许还有另一种首选方法?
答案 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);
给出了一些简单的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; }
}
和这样的动作方法:
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"}
绑定的行为与您期望的一样/希望:
可以找到关于Web API中模型绑定的好文章here。