So it seems like several people (like here and here) have had issues with MVC4 model binding for ApiControllers, but none of them seem to quite address the issue I'm seeing.
All I'd really like to do is change the array binding behavior for lists of integers. So say I had a request type like this:
public class MyRequestModel
{
public List<long> ListOfIntegers { get; set; }
...
}
And an API GET method like this:
public ResultsResponseModel Get(MyRequestModel request)
{
// use request.ListOfIntegers meaningfully
...
return response;
}
I basically want to be able to say /api/results/?listOfIntegers=1+2+3+4+5
and have that resolve to the List<long>
property.
I've tried my usual model binding tricks, but as with most of the Web API in MVC4 it appears to have a totally separate path for model binding.
The furthest I've gotten is using a System.Web.Http.ModelBinding.ModelBinder
attribute on MyRequestModel
, and creating a model binder that "implemented" System.Web.Http.ModelBinding.IModelBinder
. That consistently yields an object reference exception with stack traces that never touch my code.
Anyone hit this? Have thoughts on what to try next?
UPDATE: Here's a stack trace that I've captured in my custom ExceptionFilterAttribute
:
Object reference not set to an instance of an object.
at System.Web.Http.ModelBinding.DefaultActionValueBinder.BindParameterValue(HttpActionContext actionContext, HttpParameterBinding parameterBinding)
at System.Web.Http.ModelBinding.DefaultActionValueBinder.<>c__DisplayClass1.BindValuesAsync>b__0(RequestContentReadKind contentReadKind)
at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass38.<ToAsyncVoidTask>b__37()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
If you're talking ApiControllers, then you're trying to model bind in Web API and now MVC Here's a sample model binder
public class MyRequestModelBinderProvider : ModelBinderProvider
{
MyRequestModelBinder binder = new MyRequestModelBinder();
public IdeaModelBinderProvider()
{
}
public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(MyRequestModel))
{
return binder;
}
return null;
}
}
Here's an example of registering a custom model binder provider
IEnumerable<object> modelBinderProviderServices = GlobalConfiguration.Configuration.ServiceResolver.GetServices(typeof(ModelBinderProvider));
List<Object> services = new List<object>(modelBinderProviderServices);
services.Add(new MyRequestModelBinderProvider());
GlobalConfiguration.Configuration.ServiceResolver.SetServices(typeof(ModelBinderProvider), services.ToArray());
Now in your custom model binder you use the contexts to access the querystring values
public class MyRequestModelBinder : IModelBinder
{
public MyRequestModelBinder()
{
}
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
MyRequestModel yourModel;
//use contexts to access query string values
//create / update your model properties
bindingContext.Model = yourModel;
//return true || false if binding is successful
}
Make sure your using the classes and interfaces for WebAPI and not MVC. Some of the names are the same, but different namespaces and dlls
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With