Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC 5 Model Binder Override

Tags:

I wrote an override of the Model Binder.

public override object BindModel(Controller context, ModelBindingContext bindingContext)
{
    var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

    object returnVal = null;

    if (value == null)
        returnVal = base.BindModel(controllerContext, bindingContext);
    else
    {
        /* custom logic here that never seems to get called.
            returnVal = something();
        */
    }

    return returnVal;
}

I also have a javascript service (in Angular) that makes an AJAX request to one of my controllers.

The AJAX request attempts to post a collection of ints. I tried stepping through the model binder, and it seems like value is always null. And by some magic, the base.BindModel() is still able to bind my collection to the correct C# object.

The problem with this is that I can't use my custom binder as the else block is never invoked. Is there another way I can get the value besides using the ValueProvider?

I also believe that before this custom binder was working correctly (from memory which could be wrong). I recently updated from 4.5 to 5.2.something. Is there anything that was updated that could've changed this behavior?

like image 246
Rhs Avatar asked Mar 23 '16 17:03

Rhs


1 Answers

I also have a javascript service (in Angular) that makes an AJAX request to one of my controllers.

...

The problem with this is that I can't use my custom binder as the else block is never invoked.

  • I am going to assume that you have properly registered your binder either globally OR on the action itself on one or more of the parameters.
  • I will also assume that your binder is being called when you expect it to.

Its null because it can't find the data based on your model name that it is trying to bind to. Whether the value can be found by this name depends on the model name and the data sent in the client request which have to align/match. But before anyone can tell you why its not matching data (including your model with the array) can be sent from the client in one of three ways:

  1. If using the URL you will reuse the same property name in the query string. Example: ?myArray=1&myArray=2&myArray=3. This means that in your model binder you will have to consider this.
  2. If using the data (body) in a POST then it might be an actual array object. Example in json: {"myArray":[1,2,3,4]}
  3. You might also be serializing the entire form and sending it over with Angular (which would allow you to better use the binding functionality in MVC)

So to better answer your question you will need to provide

  1. The format of the data is being sent from the browser and how its being sent (query string or in the data payload) (this is probably in your Angular factory,service, or controller)
  2. The model definition you are trying to bind to

So to recap: bindingContext.ModelName is the expected name and must match the data that your model binder is trying to find. If you are sending {"myArray":[1,2,3,4]} but your model property is named ProductIds then it will always be null.

I recently updated from 4.5 to 5.2.something

No, not that I am aware of.


Final thought. You could also let the default model binder execute and then do something with the returned value if the types match. If the binding happens now without a problem but you want to do some post-processing this would be a better choice. Example:

public override object BindModel(Controller context, ModelBindingContext bindingContext)
{
    var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

    object returnVal = base.BindModel(controllerContext, bindingContext);

    /* check returnVal and then additional custom logic here */.
    
    return returnVal;
}
like image 86
Igor Avatar answered Oct 12 '22 10:10

Igor