1

Closed

ModelBinder registered by SimpleModelBinderProvider won't call if no query string

description

When ModelBinder is registered by SimpleModelBinderProvider and used in controller method as [ModelBinderAttribute()] it will not call if there is no query string.

For example:
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
          var pagerProvider = new SimpleModelBinderProvider(typeof(ComplexType), new ComplexTypeModelBinder());
          config.Services.Insert(typeof(ModelBinderProvider), 0, pagerProvider);

            config.MapHttpAttributeRoutes();
        }
    }

  public class ComplexType {}

  public class ComplexTypeModelBinder : IModelBinder
  {
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
      bindingContext.Model = new ComplexType();
      return true;
    }
  }

  [RoutePrefix("foo")]
  public class FooController : ApiController
  {
    [HttpGet, Route]
    public void Get([ModelBinder] ComplexType obj)
    {
      //when GET /foo obj is null //BUG?
      //when GET /foo?anyparam=true obj is not null
    }
  }

  [RoutePrefix("bar")]
  public class BarController : ApiController
  {
    [HttpGet, Route]
    public void Get([ModelBinder(typeof(ComplexTypeModelBinder))] ComplexType ct)
    {
      //when GET /foo obj is not null 
      //when GET /foo?anyparam=true obj is not null
    }
  }
Closed May 7 at 8:25 PM by yishaigalatzer
We don't think the complexity of the fix along with the limited scenarios we can cover in additional to what is supported is worth the fix.

comments

kamehrot wrote Apr 16 at 9:58 PM

Note, if you set the SuppressPrefixCheck to true, the ComplexTypeModelBinder gets called.

new SimpleModelBinderProvider(typeof(ComplexType), new ComplexTypeModelBinder()) { SuppressPrefixCheck = true };

Need to investigate why it is not getting called in the first case.

TianPan wrote May 2 at 12:35 AM

Why does public void Get([ModelBinder(typeof(ComplexTypeModelBinder))] ComplexType ct) always invoke ComplexTypeModelBinder irrespective of whether query string is null?

The ModelBinderAttribute directly sets the model binder as ComplexTypeModelBinder and it will always be invoked when hit.

Why does public void Get([ModelBinder] ComplexType obj) does not invoke ComplexTypeModelBinder if query string is null?

The ModelBinderAttribute do not know what binder to use so it creates a CompositeModelBinderProvider for you. The composite model binder (provider) contains all the binder providers from config. The pagerProvider (type of SimpleModelBinderProvider) you inserted is one of them.

CompositeModelBinder may TryBind twice. First, bind model if any query string parameter matches bindingContext.ModelName as the exact prefix. If the first binding fails, second, fall back to empty prefix.
In both cases above, no parameter value is provided from the url (query string is null), and thus no prefix exists at all. So CompositeModelBinder.BindModel will not be invoked.

If you still want to invoke it when query string is null, you can set pagerProvider.SuppressPrefixCheck = true;

Since [ModelBinder] is generally-proposed, how to set binders more specific?

e.g.

a. As the second case, always add [ModelBinder(typeof(ComplexTypeModelBinder))] to the specific parameter or to the specific type.
b. Use config.BindParameter(typeof(ComplexType), new ComplexTypeModelBinder());