Abhishek
Abhishek

Reputation: 498

Custom model binder not firing up for web api 2

So none of the existing questions answer this.

I have implemented a custom model binder for web api 2 as below

    public class AModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(Type modelType)
    {
        return modelType == typeof(A) ? new AdAccountModelBinder() : null;
    }
}

public class AModelBinder : DefaultModelBinder
{
    private readonly string _typeNameKey;

    public AModelBinder(string typeNameKey = null)
    {
        _typeNameKey = typeNameKey ?? "type";
    }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var providerResult = bindingContext.ValueProvider.GetValue(_typeNameKey);

        if (providerResult != null)
        {
            var modelTypeName = providerResult.AttemptedValue;

            SomeEnum type;

            if (!Enum.TryParse(modelTypeName, out type))
            {
                throw new InvalidOperationException("Bad Type. Does not inherit from AdAccount");
            }

            Type modelType;

            switch (type)
            {
                case SomeEnum.TypeB:
                    modelType = typeof (B);
                    break;
                default:
                    throw new InvalidOperationException("Bad type.");
            }

            var metaData =
                ModelMetadataProviders.Current
                                      .GetMetadataForType(null, modelType);

            bindingContext.ModelMetadata = metaData;
        }

        // Fall back to default model binding behavior
        return base.BindModel(controllerContext, bindingContext);
    }

Models are defined as below -

Public class A {}
Public Class B : A {}

Web Api Action as below -

        [System.Web.Http.HttpPost]
    [System.Web.Http.Route("api/a")]
    [System.Web.Http.Authorize]
    public async Task<HttpResponseMessage> Add([ModelBinder(typeof(AModelBinderProvider))]Models.A a)
{}

Registered my provider as a gentleman in Application_Start -

            var provider = new AdAccountModelBinderProvider();
        ModelBinderProviders.BinderProviders.Add(provider);

Still my custom Binder refuses to fire up.

I am lost. What am I missing?

Upvotes: 2

Views: 4218

Answers (3)

razon
razon

Reputation: 4030

may be you need to add custom parameter binding rule in WebApiConfig?

config.ParameterBindingRules.Insert(0, GetCustomParameterBinding);

see example here: How to pass ObjectId from MongoDB in MVC.net

Upvotes: 0

Kumar Lachhani
Kumar Lachhani

Reputation: 218

You need to implement IModelBinder interface:

Look at the following example of Custom:

public class MyModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        try
        {
            bindingContext.Model = new TestClass();

            //following lines invoke default validation on model
            bindingContext.ValidationNode.ValidateAllProperties = true;
            bindingContext.ValidationNode.Validate(actionContext);

            return true;
        }
        catch
        {
            return false;
        }
    }
}
  • Setting the Model Binder

There are several ways to set a model binder. First, you can add a [ModelBinder] attribute to the parameter.

public HttpResponseMessage Get([ModelBinder(typeof(MyModelBinder))] TestClass objTest)

You can also add a [ModelBinder] attribute to the type. Web API will use the specified model binder for all parameters of that type.

[ModelBinder(typeof(MyModelBinder))]
public class TestClass
{
    // ....
}

Finally, you can add a model-binder provider to the HttpConfiguration. A model-binder provider is simply a factory class that creates a model binder. You can create a provider by deriving from the ModelBinderProvider class. However, if your model binder handles a single type, it's easier to use the built-in SimpleModelBinderProvider, which is designed for this purpose. The following code shows how to do this.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var provider = new SimpleModelBinderProvider(
            typeof(TestClass), new MyModelBinder());
        config.Services.Insert(typeof(ModelBinderProvider), 0, provider);

        // ...
    }
}

With a model-binding provider, you still need to add the [ModelBinder] attribute to the parameter, to tell Web API that it should use a model binder and not a media-type formatter. But now you don't need to specify the type of model binder in the attribute:

public HttpResponseMessage Get([ModelBinder] TestClass objTestClass) { ... }

Upvotes: 2

Radek
Radek

Reputation: 1

I think, you should rather use DefaultModelBinder from namespace System.Web.ModelBinding / IModelBinder from namespace System.Web.Http.ModelBinder. I also think you did some not necessery thinks, like registering provider in Application_Start, as you are using ModelBinder directive.

For more details look at http://blog.learningtree.com/creating-a-custom-web-api-model-binder/

Upvotes: -1

Related Questions