如何替换Web API模型绑定的行为,以便在没有传入参数时,我将收到一个新实例,而不是Null

我们有一个API,其中包含许多采用Filter对象的操作。 但是,当有人调用API方法并且没有传递任何参数时,我们最终会得到一个空引用。 为了避免在任何地方检查这个问题,我们希望改变模型绑定的行为,以便对于该类型,它返回一个新实例而不是null。

另外,我们真的不想为filter类型编写自己的绑定器,因为它可能经常更改。

我们找到了一种机制,我们可以编写一个ModelBinderParameterBinding,但后来我无法弄清楚如何将该项添加到WebAPI配置中。

那么,我们是否尝试过正确的方法,如果是这样,我们如何告诉WebAPI使用我们的新参数绑定?

这里是ModelParameterBindingModel的参考……我绝对不确定这是生产代码! 因为我无法运行或测试它:)

 public class QueryFilterModelBinderParameterBinding : ModelBinderParameterBinding { private readonly ValueProviderFactory[] _valueProviderFactories; private readonly IModelBinder _binder; public QueryFilterModelBinderParameterBinding(HttpParameterDescriptor descriptor, IModelBinder modelBinder, IEnumerable valueProviderFactories) : base(descriptor, modelBinder, valueProviderFactories) { if (modelBinder == null) { throw new ArgumentNullException("modelBinder"); } if (valueProviderFactories == null) { throw new ArgumentNullException("valueProviderFactories"); } _binder = modelBinder; _valueProviderFactories = valueProviderFactories.ToArray(); } public new IEnumerable ValueProviderFactories { get { return _valueProviderFactories; } } public new IModelBinder Binder { get { return _binder; } } public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) { var ctx = GetModelBindingContext(metadataProvider, actionContext); var haveResult = _binder.BindModel(actionContext, ctx); //here's where we instantiate an empty filter if we cannot bind one var model = haveResult ? ctx.Model : new QueryFilter(); SetValue(actionContext, model); return Task.FromResult(model); } private ModelBindingContext GetModelBindingContext(ModelMetadataProvider metadataProvider, HttpActionContext actionContext) { var name = Descriptor.ParameterName; var type = Descriptor.ParameterType; var prefix = Descriptor.Prefix; var vp = new CompositeValueProviderFactory(_valueProviderFactories).GetValueProvider(actionContext); var ctx = new ModelBindingContext() { ModelName = prefix ?? name, FallbackToEmptyPrefix = prefix == null, // only fall back if prefix not specified ModelMetadata = metadataProvider.GetMetadataForType(null, type), ModelState = actionContext.ModelState, ValueProvider = vp }; return ctx; } } 

所以,我设法以稍微不同的方式解决这个问题。 虽然我仍然有兴趣看看是否有一种更惯用的解决方法!

首先,我们创建一个HttpParameterBinding

 public class QueryFilterParameterBinding : HttpParameterBinding { private readonly HttpParameterBinding _modelBinding; //private readonly HttpParameterBinding _formatterBinding; public QueryFilterParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor) { _modelBinding = new ModelBinderAttribute().GetBinding(descriptor); //_formatterBinding = new FromBodyAttribute().GetBinding(descriptor); } public override async Task ExecuteBindingAsync( ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) { await _modelBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken); var queryFilter = GetValue(actionContext) as QueryFilter; if (queryFilter == null) { queryFilter = new QueryFilter(); SetValue(actionContext, queryFilter); } } } 

并在WebApiConfig类中添加一个方法

 //returning null here tells another binding or the default binding to handle this request private static HttpParameterBinding GetQueryFilterBinding(HttpParameterDescriptor descriptor) { return descriptor.ParameterType == typeof (QueryFilter) ? new QueryFilterParameterBinding(descriptor) : null; } 

在WebApiConfig.Configure方法中我们调用:

 config.ParameterBindingRules.Add(typeof(QueryFilter), GetQueryFilterBinding)