views:

90

answers:

6

This is a fairly recurring theme on StackOverflow, but once again I can't get my MVC controller action to recognise the data I'm trying to send. Any suggestions gratefully received.

My controller action looks like this:

        [Authorize]
        [HttpPost]
        public JsonResult Record(int task, string notes, double hours)
        {
            Repository<TimeEntry> TimeRepo = new Repository<TimeEntry>();
            ...

My Ajax call looks like this:

var Task = $('#time-task option:selected').val();
var Hours = parseFloat($('#time-hours').val());
var Notes = $('#txtNotes').val();

if (isNaN(Hours)) {
    Hours = 0;
}

$.post('/Home/Record', { task: Task, notes: Notes, hours: Hours }, function (data) {
    console.log(data);
});

And the exception returned is:

The parameters dictionary contains a null entry for parameter 'task' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.JsonResult Record(Int32, System.String, Double)' in 'JobTrack.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters

There has to be something basic I'm overlooking, but damned if I can figure out where I'm going wrong. Suggestions appreciated.

Update: So changing the $.post to a $.ajax call and having that call use GET instead of POST appears to work. I suppose I can live with that but I'd rather be doing this correctly. Any suggestions as to why the HTTP Verbs should be making a difference?

A: 

You already seem to be on the right track - it thinks you aren't passing a task that can be converted to an int.

var Task = $('#time-task option:selected').val();
var Hours = parseFloat($('#time-hours').val());
var Notes = $('#txtNotes').val();

if (isNaN(Task)) {
    Task = 0;
}

if (isNaN(Hours)) {
    Hours = 0;
}

$.post('/Home/Record', { task: Task, notes: Notes, hours: Hours }, function (data) {
    console.log(data);
});

If you still have problems after this slight change, you might want to call the method with fixed parameters to test your call, before adding in the variables.

$.post('/Home/Record', { task: 1, notes: "Hello World", hours: 2 }, function (data) {
    console.log(data);
});
Sohnee
Phil.Wheeler
+1  A: 

What happens if you declare your task parameter as nullable, like:

public JsonResult Record(int? task, string notes, double hours)

Does it quell the error? Is task null if you debug that code during the call?

Dave Ward
Setting the numeric values to nullable does work, but still doesn't explain why the values aren't being passed. I can see the values being sent in the POST, but we're not reaching the method call. Any insights? I'm thinking it has to either be something wrong with the way the values are being serialised or formatted?
Phil.Wheeler
When you tried that, did *task* end up with the value that you passed in or was it null?
Dave Ward
It was null. Turns out the contentType value was set and was causing dramas with jQuery's default settings (or something).
Phil.Wheeler
The default $.post() content-type is appropriate for what you're doing there. Do you have an $.ajaxSetup() changing the default?
Dave Ward
Yeah - see my answer below. I had the $.ajaxSetup in a different file that I'd forgotten was even there. Took the declaration out and, hey presto! Things started working again.
Phil.Wheeler
A: 

Does the #time-task dropdown list have a "selected" value on page load? if not then the Task var will be null.

I would say you need to do 2 things:

  1. Set a default value for Task either on your route, controller method or in your jQuery script
  2. Then "parseFloat" the value for "Task" as this is type "Int" on your controller method
Mark
A: 

Try explicitly sending the datatype as json like this:

$.post('/Home/Record', { task: Task, notes: Notes, hours: Hours }, function (data) {
    console.log(data);
}, 'json');
ebrown
Nope. No joy there.
Phil.Wheeler
+1  A: 

Try to eliminate by simplifying. This code works (on my machine):

Controller action:

    public JsonResult TestJson(int someInt, string someString, double someDouble)
    {
        return Json(new object[]
            {
                someInt,
                someString,
                someDouble
            });
    }

In my View, I added:

<a id="testJson">testJson</a>      

and

<script type="text/javascript">
    $("#testJson").click(function() {
        $.post('/Home/TestJson', 
              { someInt: 1, someString: 'foo', someDouble: 1.1}, 
              function(data) {
                  alert(data);
              });
    });    
    </script>
jeroenh
Good plan, but exactly the same result.
Phil.Wheeler
A: 

ARGH!

Knew it would be something basic I'd overlooked.

The root cause of the problem was the /Scripts/globals.js file that I haven't even been looking at (and had pretty much forgotten completely about). In that, I had the following code:

$.ajaxSetup({
    type: "POST",
    contentType: "application/json; charset=utf-8",
    data: "{}",
    dataFilter: function(data) {
        var msg;

        if (typeof (JSON) !== 'undefined' &&
        typeof (JSON.parse) === 'function')
            msg = JSON.parse(data);
        else
            msg = eval('(' + data + ')');

        if (msg.hasOwnProperty('d'))
            return msg.d;
        else
            return msg;
    }
});

The problem? ContentType. My understanding of the root cause is that since I'm using jQuery 1.4, the content type is being correctly determined automatically ("application/x-www-form-urlencoded") and expressly defining it again in the global settings must confuse things somehow.

Thanks for all who contributed to this solution. If anyone knows why the contentType causes Ajax posts to fail in jQuery, I'd love to get a deeper understanding.

Phil.Wheeler