How can I call custom model binding in MVC4?

It seems that a few people (for example, here and here ) have encountered problems with binding the MVC4 model for ApiControllers , but none of them seem to cope with the problem that I see.

All I really would like to do is change the array binding behavior for integer lists. So say that I had a request type:

public class MyRequestModel { public List<long> ListOfIntegers { get; set; } ... } 

And a GET API method like this:

 public ResultsResponseModel Get(MyRequestModel request) { // use request.ListOfIntegers meaningfully ... return response; } 

Basically I want to say /api/results/?listOfIntegers=1+2+3+4+5 and have this solution for the List<long> property.

I tried my usual model-bound tricks, but, like most web APIs in MVC4, it has a completely separate model-binding path.

The most that I got is to use the System.Web.Http.ModelBinding.ModelBinder attribute on MyRequestModel and create a binding to a model that is "implemented" by System.Web.Http.ModelBinding.IModelBinder . This consistently gives an object reference exception with stack traces that never touch my code.

Has anyone hit this? Any thoughts on what to try next?

UPDATE Here's the stack trace I grabbed 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) 
+5
asp.net-mvc asp.net-web-api asp.net-mvc-4
source share
1 answer

If you say ApiControllers, then you are trying to model the binding in the web API and now MVC. Here's an approximate 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 is an example of registering a custom model binding 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 connector, you use contexts to access query 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 you use the classes and interfaces for WebAPI, not MVC. Some names are the same, but different namespaces and dlls

+4
source share