Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Posting dynamic JSON to an MVC4 Action

With webapi it is possible to POST dynamic JSON to an action using either JObject or dynamic as your parameter type:

Passing Dynamic JSON Object to Web API - Newtonsoft Example

If I try this on a non-api action in MVC4 this doesn't seem to work. My action signature is:

public async Task<ActionResult> Post(JObject requestObj)

When I use dynamic I just get a seemingly non-dynamic object. If I try JObject I get the following error:

[MissingMethodException]: Cannot create an abstract class.

Is something similar to this possible on a non-api action in MVC4?

like image 652
mutex Avatar asked Sep 06 '25 00:09

mutex


2 Answers

In a previous project we needed to post dynamic JSON to a Web Api controller. What we ended up doing was taking the JSON on the client side and Base64 encode it. We could then simply post the Base64 encoded JSON to our backend. Our backend then decoded the Base64 input and used Newtonsoft JSON to convert it into a dynamic object (actually it was converted to strongly typed classes and the fallback was dynamic). I agree, it's hacky, but it worked.

like image 116
Martin Avatar answered Sep 07 '25 20:09

Martin


I'm not sure if it will be helpful at all, but I mentioned in my comments above that I ended up using a custom model binder to do this. I dug up what I believe was the original code that prompted this question, and this is what I ended up with:

    public async Task<ActionResult> Post(dynamic request)
    {
        return await ExecuteRequest(request, "application/json");
    }

and a custom model binder as follows (it works on actions called "post" or "public" though you could choose your own convention - and falls back to default on all other actions)

public class MyModelBinder : DefaultModelBinder
{        
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var actionName = controllerContext.RouteData.Values["Action"];
        if (controllerContext.Controller.GetType() == typeof(MyController) && actionName != null && 
            (string.Compare(actionName.ToString(), "post", StringComparison.OrdinalIgnoreCase) == 0 ||
            string.Compare(actionName.ToString(), "public", StringComparison.OrdinalIgnoreCase) == 0))
        {
            string contentText;

            using (var stream = controllerContext.HttpContext.Request.InputStream)
            {
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream))
                    contentText = reader.ReadToEnd();
            }

            if (string.IsNullOrEmpty(contentText)) return (null);

            return JObject.Parse(contentText);
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}

Then register the custom model binder in the beginning of Application_Start:

System.Web.Mvc.ModelBinders.Binders.DefaultBinder = new MyModelBinder();
like image 42
mutex Avatar answered Sep 07 '25 21:09

mutex