Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Posting JSON to Controller

I've got this in my controller:

[HttpPost]
public void UpdateLanguagePreference(string languageTag) {
 if (string.IsNullOrEmpty(languageTag)) {
  throw new ArgumentNullException("languageTag");
 }
 ...
}

And have this jQuery code POSTing to the controller:

jQuery.ajax({
  type: 'POST',
  url: '/Config/UpdateLanguagePreference',
  contentType: 'application/json; charset=utf-8',
  data: '{ "languageTag": "' + selectedLanguage + '" }'
});

When I try to the code, however, I get the error:

Server Error in '/' Application.
Value cannot be null.
Parameter name: languageTag

What's the problem? Isn't this how to POST JSON to an MVC Controller? I can examine the POST using Fiddler and see that the request is correct. For some reason, UpdateLanguagePreference() is getting a null or empty string.

like image 300
JamesBrownIsDead Avatar asked May 18 '10 21:05

JamesBrownIsDead


People also ask

How pass JSON data to AJAX to controller?

Create target "JSON object Mapper" object class file according to the business requirements. Create a "Controllers\HomeController. cs" file with default Index method and GetData(...) method with string type input query parameters for Ajax call with following lines of code i.e.

How do I POST JSON to a REST API endpoint?

To post JSON to a REST API endpoint, you must send an HTTP POST request to the REST API server and provide JSON data in the body of the POST message. You also need to specify the data type in the body of the POST message using the Content-Type: application/json request header.

Can we send JSON object in post request?

Specify the POST data: As per the HTTP specification for a POST request, we pass data through the message body. Using requests, you'll pass the payload to the corresponding function's data parameter. Data can be anything including JSON, dictionary, a list of tuples, bytes, or a file-like object.


2 Answers

Very important caveat, even in MVC3, about the way MVC3 works.

If you pass an object like say:

​{
    Test: 'Hi'
}

And the receiving class is:

public class MyModel
{
    public string Test { get; set; }
}

With a receiving Controller method like:

[HttpPost]
public JsonResult Submit(MyModel model)
{
    . . .

It will work. But if your Controller method has this very minor, seemingly harmless change:

[HttpPost]
public JsonResult Submit(MyModel test)
{
    . . .

It will fail. This is because the MVC framework consumes the JSON into a Dictionary as mentioned above, and sees that one of the parameters has the same name, case-insensitive, as one of its keys ("Test"/"test"). It then takes the string value "Hi", assigned to Test, and passes that to this argument "test" even though this is obviously not what the author intended.

What's most problematic about this is that the framework doesn't throw an error trying to assign a string to an arg of type MyModel, which would at least give you a clue about what went wrong. It doesn't see this was the wrong type and fallback to its alternative behavior (that it would have pursued had these arg/properties not matched in name). It simply fails silently and assigns null to your argument, leaving you with no clue as to what's going on.

I've run into this problem repeatedly and finally found the glitch that makes things seem to randomly stop working in MVC... I hope this helps someone else.

Since any reasonable name for this Action argument is a potentially reasonable property name (model, data, etc) and since it's case-insensitive, the safest way to prevent it without writing your own model binder is to standardize on one, crazy, very-unlikely-to-ever-be-a-property-name argument name, like:

[HttpPost]
public JsonResult Submit(MyModel _$_$twinkleTwinkleLittleFuckIt)
{

But if you have the time, fix the ModelBinder/JsonValueProviderFactory so there's 0 risk instead of that one weird bug in a decade no one can ever get to the bottom of.

like image 158
Chris Moschini Avatar answered Oct 21 '22 02:10

Chris Moschini


hmm....

I do it

 $.post(target,
         {
             "ProblemId": id,
             "Status": update
         }, ProcessPostResult);

with

public class ProblemReportUpdate
    {
        public int ProblemId { get; set; }
        public string Status { get; set; }
    }

and

 [HttpPost]
 public ActionResult UpdateProblemReport(ProblemReportUpdate update)

the target is set by

var target = '<%=Url.Action("UpdateProblemReport", "ProblemReport") %>
like image 39
Keith Nicholas Avatar answered Oct 21 '22 03:10

Keith Nicholas