views:

542

answers:

1

I want to be able to send JSON as opposed to the standard QueryStrings when making a post to my controllers in ASP.Net MVC. I have the Front-End stuff working fine (building and then submitting my JSON objects).

The problem is on the controller side where the default ModelBinders that ship with the MVC framework do not support this.

I have seen a combination of ways around this, one of them is to apply a filter which takes the object as a parameter, uses a JSON library to de-serialise it, and adds that to the action parameters. This is not ideal.

The other, better, way is to use a custom Model Binder. All the ones I have seen though presume you will have only one model and that will be a class rather than a variable. If you have multiple ones it breaks down.

Has anyone else encountered this? One idea I had was if I could simply override how MVC deals with the FormCollection and intercept there, adding the values to the collection myself and hoping MVC can do the rest in it's normal fashion. Does anyone know if that is possible?

The key issue, I think, is that my problem is not with binding because my view models are no different to how they where before. The problem is getting the values from the JSON Post.

If I am correct MVC get's the values from the QueryString and puts it into the form collection which is then used for ModelBinding. So shouldn't the correct method be to change the way the FormCollection gets assigned?

Example of an action:

public ActionResult MyFirstAction(Int32 ID, PersonObject Person, ClassObject ClassDetails)
{
//etc
}

The normal binding works, JSON doesn't and all the example of Model Binders will not work either. My best solution so far is to convert the object to a dictionary and loop though each param and match it up. Doesn't seem ideal.

+4  A: 

I use a custom model binder for json like this:

public class JsonModelBinder<T> : IModelBinder {
    private string key;

    public JsonModelBinder(string requestKey) {
        this.key = requestKey;
    }

    public object BindModel(ControllerContext controllerContext, ...) {
        var json = controllerContext.HttpContext.Request[key];
        return new JsonSerializer().Deserialize<T>(json);
    }
}

And then wire it up in Global.asax.cs like this:

ModelBinders.Binders.Add(
    typeof(Product),
    new JsonModelBinder<Product>("ProductJson"));

You can read more about this here: Inheritance is Evil: The Epic Fail of the DataAnnotationsModelBinder

EDIT

The JsonModelBinder should be used on the controller action parameter typed as Product only. The Int32 and ClassObject should fall back to the DefaultModelBinder. Are you experiencing a different result?

Michael Valenty
So you would need to define which types to use it for?
Damien
Yes, you have to specify the types. Can you describe your situation in a little more detail? I have been digging into model binding more lately and there is a lot of non-obvious functionality in it.
Michael Valenty
Well with the types what do I do about action parameters which are primitive types/stings? If I am sending Int32 ID, Class Product it creates a problem
Damien
Can you update your question with an example controller action?
Michael Valenty
No Problemo! Added it. Cheers.
Damien
Yes, I am (experiencing a different result). Basically the JSON object that gets sent to the action is not populating anything. If I sent { "ID" : 34 } and ID was the only action it won't get mappped. These are normal entities, it's just that I want to send the data via JSON and MVC doesn't understand it.
Damien