12

My iphone client posts the following json to my mvc service. When posting data from html form it automatically converts form data to UserModel and passes the object to my Create method, but when I send the JSON string in the body of my request from iphone it comes back as null.

What would be the cleanest solution to do the conversion from JSON to Object.

I rather not create multiple methods for different clients, so I'm trying to get the same method to work on iphone, and mvc client.

Body of my request:

{
   "firstName" : "Some Name",
   "lastName" : "Some Last Name",
   "age" : "age"
}

My Model and Action Result

public class UserModel
{
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public int Age { get; set; }
}

[HttpPost]
public Create ActionResult(UserModel user)
{
   // user is null
   userStorage.create(user);
   return SuccessResultForModel(user);
}
Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
aryaxt
  • 76,198
  • 92
  • 293
  • 442
  • just to confirm, you want to convert the user object that hits the controller from json? – Dave Alperovich Mar 10 '13 at 01:50
  • Did you register the JsonValueProviderFactory (which is needed for MVC to be able to decode the JSON post data)? Does the client specify the correct ContentType? See http://stackoverflow.com/questions/4789481/post-an-array-of-objects-via-json-to-asp-net-mvc3 for additonal things to check (top voted answer has the list). – Morten Mertner Mar 10 '13 at 01:51
  • That JSON doesn't look valid, there's a comma after "age"? Also, the value for age should be an int? (Although I think failing that, it will use a default int value of 0). Also, your HttpPost method says `Create Actionresult`, shouldn't it be `ActionResult Create`? – Snixtor Mar 10 '13 at 02:00
  • @DaveA No I haven't registered, I'll give ti a try and post back thanks – aryaxt Mar 10 '13 at 03:27

4 Answers4

21

You need to set the HTTP Header, accept, to 'application/json' so that MVC know that you as passing JSON and does the work to interpret it.

accept: application/json

see more info here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

UPDATE: Working sample code using MVC3 and jQuery

Controller Code

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public JsonResult PostUser(UserModel data)
        {
            // test here!
            Debug.Assert(data != null);
            return Json(data);
        }
    }

    public class UserModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
    }
}

View Code

@{ ViewBag.Title = "Index"; }
<script src="../../Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
    var sample = {};
    sample.postData = function () {
        $.ajax({
            type: "POST", url: "@Url.Action("PostUser")",
            success: function (data) { alert('data: ' + data); },
            data: JSON.stringify({ "firstName": "Some Name", "lastName": "Some Last Name", "age": "30" }),
            accept: 'application/json'
        });
    };
    $(document).ready(function () {
        sample.postData();
    });
</script>

<h2>Index</h2>

** Update ** I added JSON.stringify to the JS object before I pass it to the data element in the AJAX Request. This just makes the payload more readable, however the Controller will interpret both formats of the data similarly.

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
  • 3
    But the accept header is only to tell the server what the client is able to receive. As described in other questions it's sufficient to set the content type header: `contentType: "application/json; charset=utf-8"`, – Reiner Sep 07 '17 at 09:09
  • 1
    This answer doesn't seem right to me. The code given does 'work', but if you look at the actual body of the request sent by $.ajax() its not sending JSON, its using application/x-www-form-urlencoded format, which is querystring format basically. If you change the jQuery code to actually send JSON, the MVC action no longer works because the contentType is not being set. See my answer here https://stackoverflow.com/a/52205206/22194 for a slightly different version that does send JSON and does work. – codeulike Sep 06 '18 at 13:41
  • @codeulike i read your other answer. if you want to set the contentType of the returned conten, ASP.NET (and more modern versions since this post offer that capability. – Glenn Ferrie Sep 07 '18 at 04:13
  • @AaronLS the sample code in the question was using the `HttpPost` attribute on the MVC Action. If you remove that, you can access the endpoint with a HTTP Get, but we would also have to change the last line of the action, where we return the content, like this `return Json(data, JsonRequestBehavior.AllowGet);` -- by default I do not believe that MVC Actions can return JSON from an HTTP Get request. – Glenn Ferrie May 09 '19 at 17:50
  • The raw request body in this answer posts form data, not json: `firstName=Some+Name&lastName=Some+Last+Name&age=30`. That javascript will take the JSON object passed to `data:` and serialize it as this form post. I just want others to be aware this is not how you'd actually testing JSON model binding, because it's not actually sending a JSON as the body of the HTTP Post. – AaronLS May 09 '19 at 19:59
  • This was a while ago, but just following up. I think to get the body to be JSON we'd need a `JSON.stringify` in there. I will update the response – Glenn Ferrie Feb 03 '21 at 04:21
2

I recently came up with a much simpler way to post a JSON, with the additional step of converting from a model in my app. Note that you have to make the model [JsonObject] for your controller to get the values and do the conversion.

Request:

 var model = new MyModel(); 

 using (var client = new HttpClient())
 {
     var uri = new Uri("XXXXXXXXX"); 
     var json = new JavaScriptSerializer().Serialize(model);
     var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
     var response = await Client.PutAsync(uri,stringContent).Result;
     ...
     ...
  }

Model:

[JsonObject]
[Serializable]
public class MyModel
{
    public Decimal Value { get; set; }
    public string Project { get; set; }
    public string FilePath { get; set; }
    public string FileName { get; set; }
}

Server side:

[HttpPut]     
public async Task<HttpResponseMessage> PutApi([FromBody]MyModel model)
{
    ...
    ... 
}
Dustin
  • 735
  • 1
  • 7
  • 16
1

Late but hope it helps someone.

What would be the cleanest solution to do the conversion from JSON to Object?

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

$.post("http://localhost:52161/Default/PostRawJson/", { json: {
   "firstName" : "Some Name",
   "lastName" : "Some Last Name",
   "age" : "age"
}});


public void PostRawJson(string json)
{
    var person = System.Web.Helpers.Json.Decode(json);
    person.firstname...
}

This way you get a pure JSON object to work with in your controller as requested.

David Ulvmoen
  • 87
  • 1
  • 3
  • This helped me a lot when I was trying to post json of FormCollection from controller to some other service. Thanks. – Anoop H.N Jul 27 '17 at 10:02
  • 1
    this does actually work and I like that you can see where the decoding happens, rather than it happening by magic in the mvc model binder. – codeulike Sep 06 '18 at 12:49
1

Based on Glenn Ferries answer, which seemed to work until I looked at what was actually being sent to the controller - and it wasn't JSON.

Hence adjusted a bit below:

This is with MVC 4, and jQuery 1.7 ish

Controller action method (UserModel object as in question):

[HttpPost]
public ActionResult Create(UserModel user)
{
    Debug.Assert(data != null);
    return Json(data);
}

jQuery code to send the request:

<script>
    $(function () {
        $.ajax({
            type: "POST",
            url: "@Url.Action("Create")",
            /* jQuery will not send JSON unless you explicitly tell it to */
            data: JSON.stringify({ "firstName": "Some Name", "lastName": "Some Last Name", "age": "30" }),
            /* contentType is important for MVC to be able to unpack the json */
            contentType: 'application/json'
            accept: 'application/json',
        }).done( function(data) {
            /* this callback gets used when successful */
            console.log("response: " + data);
        }).fail(function (jqxhr, status, error) {
            /* for debugging: this callback gets used in case of errors */
            console.log("error :" + error);
        }).always(function () {
            /* for debugging: this callback always gets called at the end either way*/
            console.log("complete");
        });
    });

</script>

The MVC controller action is the easy bit.

The hard bit is making sure you actually are sending JSON to it to test it.

jQuery ajax() method when used with POST will generally encode parameters using querystring format in the body of the request. MVC can happily unpack that.

To actually send JSON with ajax() you must use JSON.stringify().

And for MVC to correctly interpret that request, you need to set the contentType

codeulike
  • 22,514
  • 29
  • 120
  • 167