106

I'm a novice web programmer so please forgive me if some of my "jargon" is not correct. I've got a project using ASP.NET using the MVC3 framework.

I am working on an admin view where the admin will modify a list of equipment. One of the functions is an "update" button that I want to use jquery to dynamically edit the entry on the webpage after sending a post to the MVC controller.

I presume this approach is "safe" in a single admin setting where there is minimal concern of the webpage getting out of sync with the database.

I've created a view that is strongly typed and was hoping to pass the model data to the MVC control using an AJAX post.

In the following post, I found something that is similar to what I am looking at doing: JQuery Ajax and ASP.NET MVC3 causing null parameters

I will use the code sample from the above post.

Model:

public class AddressInfo 
{
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Check(AddressInfo addressInfo)
    {
        return Json(new { success = true });
    }
}

script in View:

<script type="text/javascript">
var ai = {
    Address1: "423 Judy Road",
    Address2: "1001",
    City: "New York",
    State: "NY",
    ZipCode: "10301",
    Country: "USA"
};

$.ajax({
    url: '/home/check',
    type: 'POST',
    data: JSON.stringify(ai),
    contentType: 'application/json; charset=utf-8',
    success: function (data.success) {
        alert(data);
    },
    error: function () {
        alert("error");
    }
});
</script>

I have not had a chance to use the above yet. But I was wondering if this was the "best" method to pass the model data back to the MVC control using AJAX?

Should I be concerned about exposing the model information?

Community
  • 1
  • 1
John Stone
  • 1,375
  • 4
  • 13
  • 16

5 Answers5

178

I found 3 ways to implement this:

C# class:

public class AddressInfo {
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
}

Action:

[HttpPost]
public ActionResult Check(AddressInfo addressInfo)
{
    return Json(new { success = true });
}

JavaScript you can do it three ways:

1) Query String:

$.ajax({
    url: '/en/Home/Check',
    data: $('#form').serialize(),
    type: 'POST',
});

Data here is a string.

"Address1=blah&Address2=blah&City=blah&State=blah&ZipCode=blah&Country=blah"

2) Object Array:

$.ajax({
    url: '/en/Home/Check',
    data: $('#form').serializeArray(),
    type: 'POST',
});

Data here is an array of key/value pairs :

=[{name: 'Address1', value: 'blah'}, {name: 'Address2', value: 'blah'}, {name: 'City', value: 'blah'}, {name: 'State', value: 'blah'}, {name: 'ZipCode', value: 'blah'}, {name: 'Country', value: 'blah'}]

3) JSON:

$.ajax({
      url: '/en/Home/Check',
      data: JSON.stringify({ addressInfo:{//missing brackets
          Address1: $('#address1').val(),
          Address2: $('#address2').val(),
          City: $('#City').val(),
          State: $('#State').val(),
          ZipCode: $('#ZipCode').val()}}),
      type: 'POST',
      contentType: 'application/json; charset=utf-8'
});

Data here is a serialized JSON string. Note that the name has to match the parameter name in the server!!

='{"addressInfo":{"Address1":"blah","Address2":"blah","City":"blah","State":"blah", "ZipCode", "blah", "Country", "blah"}}'
Guruprasad J Rao
  • 29,410
  • 14
  • 101
  • 200
Jazaret
  • 3,655
  • 3
  • 17
  • 15
  • 1
    Just came across this great, thorough answer which solved questions I didn't know I had yet. +1, thanks! – SeanKilleen Jul 21 '13 at 03:04
  • #2 was what I was looking for. This should be the answer. – TheGeekZn Jul 30 '13 at 10:07
  • EDIT: had to use `data: $('input, textarea, select').serialize(),` for mine to work. – TheGeekZn Jul 30 '13 at 10:37
  • Hey Jazaret!! how to pass date to model with 3rd approach?? – Guruprasad J Rao Feb 12 '15 at 18:49
  • 1
    Sorry for the delay @GuruprasadRao To pass a date you can have the date & time be a string in the javascript code and MVC will translate it to a DateTime object. – Jazaret Mar 03 '15 at 14:40
  • @Jazaret Do the properties have to be in the same order as they are in the viewmodel? – tcrite May 04 '15 at 22:01
  • @tcrite I don't believe they need to be in the same order. – Jazaret May 05 '15 at 16:13
  • @Jazaret Thanks. I figured out my issue. Some of my property names weren't matching up in a collection that was a property of the serialized object. – tcrite May 05 '15 at 21:45
  • When using method 3 (JSON), the name of the parameter in the controller action is not required i.e. in this case 'addressInfo:', only { } is enough. Probably needed in the case of multiple action parameters. – BiLaL Oct 25 '15 at 17:10
  • is there any way to get intellisense to avoid the typos which are very much possible as happened with @tcrite. – BiLaL Oct 25 '15 at 17:12
  • Option number 3 saved my life! The name must match the parameter name in the controller function was throwing me off for days! Thanks for this answer, should be the selected answer. – BradStell Apr 25 '16 at 00:43
  • This is a bit late in the game but I'd argue you shouldn't use option 1 because a POST request shouldn't be exposing values in the query string. It could also be viewed as a security vulnerability. – Charles Owen Apr 26 '17 at 03:43
75

You can skip the var declaration and the stringify. Otherwise, that will work just fine.

$.ajax({
    url: '/home/check',
    type: 'POST',
    data: {
        Address1: "423 Judy Road",
        Address2: "1001",
        City: "New York",
        State: "NY",
        ZipCode: "10301",
        Country: "USA"
    },
    contentType: 'application/json; charset=utf-8',
    success: function (data) {
        alert(data.success);
    },
    error: function () {
        alert("error");
    }
});
Sandor Drieënhuizen
  • 6,310
  • 5
  • 37
  • 80
Craig M
  • 5,598
  • 4
  • 32
  • 43
  • Thanks for pointing out the slight adjustment. Is there any concern with exposing the model structure from a security standpoint? – John Stone May 12 '11 at 18:28
  • Nothing glaring stands out as a security issue to me. If you're really concerned about it though, you can always make a custom model binder on the mvc side. – Craig M May 12 '11 at 18:31
  • 10
    This failed for me. I had to use JSON.stringify( { ... } ) for the call to work in MVC5. – Johncl Mar 11 '15 at 15:39
  • I've noticed that I have to do the same when working with API controllers. This answer was written 4 years ago though, before API controllers existed. – Craig M Mar 12 '15 at 22:16
  • 1
    god damit, I had dataType instead of contentType, that one always always gets me!! – Phil Jan 30 '17 at 11:45
12

This is the way it worked for me:

$.post("/Controller/Action", $("#form").serialize(), function(json) {       
        // handle response
}, "json");

[HttpPost]
public ActionResult TV(MyModel id)
{
    return Json(new { success = true });
}
Sanchitos
  • 8,423
  • 6
  • 52
  • 52
8

what you have is fine - however to save some typing, you can simply use for your data


data: $('#formId').serialize()

see http://www.ryancoughlin.com/2009/05/04/how-to-use-jquery-to-serialize-ajax-forms/ for details, the syntax is pretty basic.

Adam Tuliper
  • 29,982
  • 4
  • 53
  • 71
  • In order to use the serialize function, my understanding is that each member of the class needs to be used in a form object. If that is correct, I may be SOL. – John Stone May 12 '11 at 18:31
  • 1
    ah ya.. if not you cant use serialize then. you can always manipulate the DOM though and create a form with those elements and serialize it - but... it would likely be cleaner to just have the fields manually typed out then. – Adam Tuliper May 12 '11 at 18:59
  • @TahaRehmanSiddiqui serialize indeed works in IE, what doesn't work? Do you get an error? – Adam Tuliper Oct 27 '13 at 00:26
  • every property of my model is coming out null – Taha Rehman Siddiqui Oct 27 '13 at 07:26
  • @TahaRehmanSiddiqui do the 'name' of your form fields match the names of your model properties? – MongooseNX Nov 16 '13 at 17:01
  • found my problem, I actually had all the properties listed as the function's parameters. And that does not work in IE. I have not tried receiving a model directly in the function. – Taha Rehman Siddiqui Nov 16 '13 at 20:14
0

If using MVC 5 read this solution!

I know the question specifically called for MVC 3, but I stumbled upon this page with MVC 5 and wanted to post a solution for anyone else in my situation. I tried the above solutions, but they did not work for me, the Action Filter was never reached and I couldn't figure out why. I am using version 5 in my project and ended up with the following action filter:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Filters;

namespace SydHeller.Filters
{
    public class ValidateJSONAntiForgeryHeader : FilterAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get(KEY_NAME);
            if (clientToken == null)
            {
                throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
            }

            string serverToken = filterContext.HttpContext.Request.Cookies.Get(KEY_NAME).Value;
            if (serverToken == null)
            {
                throw new HttpAntiForgeryException(string.Format("Cookies does not contain {0}", KEY_NAME));
            }

            System.Web.Helpers.AntiForgery.Validate(serverToken, clientToken);
        }

        private const string KEY_NAME = "__RequestVerificationToken";
    }
}

-- Make note of the using System.Web.Mvc and using System.Web.Mvc.Filters, not the http libraries (I think that is one of the things that changed with MVC v5. --

Then just apply the filter [ValidateJSONAntiForgeryHeader] to your action (or controller) and it should get called correctly.

In my layout page right above </body> I have @AntiForgery.GetHtml();

Finally, in my Razor page, I do the ajax call as follows:

var formForgeryToken = $('input[name="__RequestVerificationToken"]').val();

$.ajax({
  type: "POST",
  url: serviceURL,
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  data: requestData,
  headers: {
     "__RequestVerificationToken": formForgeryToken
  },
     success: crimeDataSuccessFunc,
     error: crimeDataErrorFunc
});
blubberbo
  • 4,441
  • 4
  • 23
  • 36
  • 1
    Are you retrieving all your form values manually? Why not `data: $("#the-form").serialize()`? – Sinjai Aug 24 '17 at 15:59
  • 1
    @Sinjai I would have to look at my code again, but I believe I am doing some other processing in there as well. ".serialize()" would also work if you just need the input values – blubberbo Aug 24 '17 at 16:30
  • No worries, I was just curious. – Sinjai Aug 24 '17 at 16:31