Webcognoscere

In this post I'll show you how to send a JSON object to your MVC Action Method and have your Action Method return you a JSON result.

The code for this article can be downloaded from here.

Why do you want this?

Firstly I'd like to give you some examples as to why you would want to do this and how it can enhance your web applications/sites:-

  1. You want a web page to save some data to a database without having to redirect the user to another page or have the user leave the current page they are on.
  2. You want to perform some or other function on the server side without having the user leave the current page.
  3. You want to keep the UI responsive
  4. You want to minimize the amount of postbacks on your website.

 

Understanding the scenario

My scenario is a simple one:-
We have a website that sells various items online. Our store currently has only three items. To keep this page responsive I want to keep items that the user opts to add to his cart in a client side object, until such time that they want to actually check out and pay for these items.
I want to code it this way as the alternative is to save these items each time the user clicks the 'Add to Cart' button. This would then post back each and every time the user clicks the 'Add to Cart' button and could prove slow and frustrating for the user. So to keep our users engaged with our site we must provide them with a responsive and efficient UI and storing his/her cart items in a client-side JSON object is the perfect mechanism for doing this. Adding and removing items from his/her cart will be quick and when they are ready to purchase they can click the 'checkout' option.

 

First things first, defining the Shopping Cart Objects

For this article I specifically wanted a semi-complex object as the mapping of JSON objects to C# objects in previous versions of MVC worked only for what I call 1 tier objects. Objects without child objects inside them. Things like arrays were a hassle to get working and you had to write your own custom binding helpers to get them to map properly. With the advent of MVC 3, this is much easier, but I still wanted to show you what a real life object may look like and how it would map into your C# objects.

So here below you can see what my Shopping Cart objects look like:

C# Objects

public class ShoppingCartModel
{
	public int UserId { get; set; }
	public string DeliveryInstructions { get; set; }
	public List<CartItemModel> CartItems { get; set; }
}

public class CartItemModel
{
	public int Id { get; set; }
	public string ItemName { get; set; }
	public decimal Price { get; set; }
	public int Quantity { get; set; }
}

ShoppingCartModel is the 'parent' object that holds a list of the Items that the user wants to purchase.

Now in our scenario we need a JSON representation of our C# objects so we can use them on the UI when the user is adding/removing items from his/her cart.

JSON Object

var m_ShoppingCart = {
	UserId: 10,
	DeliveryInstructions: "Leave at front desk",
	CartItems: []
};

I've just put random values inside this object to show you what data would typically be there.

Next we need to create a simple UI so that we can add items to our JSON object (m_ShoppingCart) to pass through to our Controller Action Method. I'll just show you the output of what the UI looks like below and you can just grab the code here.

So the idea here is to click on the 'Add to Cart' button, have this add the item to the JSON object called m_ShoppingCart, then have that get added to the Shopping Cart UI List (which calculates the total) and finally have the user click the Checkout.
When the user clicks the 'Proceed to Checkout' link, we post this JSON object to the Controller Action Method, which converts our JSON object into our C# object where we can then save it to the database and return a success status indicator. 

The JavaScript

Adding to the Cart

Calling the AddToCart function

<input type="button" value="Add to Cart" onclick="javascript:AddToCart(1, 'Book A', 14.95, 1);$(this).attr('disabled',true);" />

You'll notice that I call two Javascript functions in the 'onclick' event of the button. The first is the function that actually adds the item to the shopping cart, the second merely disables the button so that the user cannot click it again.
Next, the function itself

function AddToCart(id, itemName, price, quantity) {

	//  Add the item to the shopping cart object
	m_ShoppingCart.CartItems.push({
		"Id": id,
		"ItemName": itemName,
		"Price": price.toFixed(2), //   Issue here if you don't specify the decimal place
		"Quantity": quantity
	});

	//  Render the shopping cart object
	RenderShoppingCart();
}

On the face of it, AddToCart() is a pretty straight forward method, however, there's two items of interest inside it.
Firstly, there's the JavaScript 'push' method, which is a native JavaScript method for adding items to an array. This method is especially useful when adding complex objects to an array.
Secondly, you may have noticed that I specifically call the 'toFixed()' method when assigning the price to the m_ShoppingCart.Price property. This is because the toFixed() method preserves the number of decimals in the price that I'm passing through. Without it, my JSON object would store the number 11 as opposed to 11.00 and, when it got passed through to my controller action method, the mapping into the C# discarded the actual number and merely retained the trailing zeros, resulting in a price of $0, not $11.00. (if anyone can explain this to me, that'd be great)

Rendering the Cart

Rendering the cart is straight forward, however, I'll list it here for those of you that would like to see how I built up the html using javascript and jQuery:

function RenderShoppingCart() {

	$("#CartItemList").html("");

	var totalAmount = 0;

	$.each(m_ShoppingCart.CartItems, function (index, cartItem) {

	    var itemTotal = Number(cartItem.Price) * Number(cartItem.Quantity);
	    totalAmount += itemTotal;

	    $("#CartItemList").append("<li>" + cartItem.ItemName + " - $" + itemTotal.toFixed(2) + "</li>");
	});

	$("#TotalAmount").html("$" + totalAmount.toFixed(2));
}


The Meat and Potatoes

Next we have what I like to call the 'Meat and Potatoes' of the post :) the actual Posting of the JSON object to our MVC Controller Action Method:

The JavaScript

function PostData() {
		$.ajax({
			url: '@Url.Content("~/Store/Checkout")',
			async: false,
			type: "POST",
			data: JSON.stringify(m_ShoppingCart), 
			dataType: "json", 
			contentType: "application/json; charset=utf-8",
			error: function (jqXHR, textStatus, errorThrown) {
				alert(jqXHR + "-" + textStatus + "-" + errorThrown);
			},
			success: function (data, textStatus, jqXHR) {
				$("#Results").show();
				$("#ResultMessage").html(data.Message);
			}
		});
}

Ok, so this is the crux of this post and certainly took me some time to figure out the right combination of the parameters.

So, what are the parameters and what values should I give them:

  • async
    • set this to 'true' (without the apostrophes) to prevent the page for 'waiting' for this method to return. I've set it to false as I wanted my page to wait till the post completed.
  • data (with JSON.stringify)
    • this is where you specify the data that is going to be sent to our Action Method
    • WHAT IS JSON.stringify
      • If you leave the JSON.stringify method out, your object (m_ShoppingCart) will be sent to your Action Method as a Key/Value pair object, i.e. a series of objects that have a string representing the name of your property and the value thereof, e.g.:
        • UserId=10
        • DeliveryInstructions=Leave+at+front+desk
        • CartItems[0][Id]=1&CartItems[0][ItemName]=Book A&CartItems[0][Price]=14.95&CartItems[0][Quantity]=1&CartItems[1][Id]=2 ........etc...
      • Using the JSON.stringify method ensures that your object is sent as a JSON object to your Action Method (a string representation thereof)
      • It is a native Javascript method (as of JavaScript 1.7 afaik) that converts a value (object) to the JSON string representation of that object
      • Must work in conjunction with the contentType parameter below
  • dataType
    • This specifies the type of data you are expecting from the server. (i.e. not the datatype that you are sending to the server :) )
    • Our example sees us receiving a JSON object, so you specify "json" as the dataType
  • contentType
    • This gave me pains in my side as you must set this to 'application/json' to inform the server what data type you are sending to the server. If you don't set this, the default is 'application/x-www.form-urlencoded' and the server will interpret the data you are sending it as key/value pairs.
    • Why did I set "charset=utf-8". By default it should be UTF-8 (at least as per jQuery.ajax documentation), but just in case ;)

The C#

My controller doesn't do much, it accepts the ShoppingCart object that you send through from the JavaScript, compiles a nice 'success message' and returns this to the caller.

[HttpPost]
public JsonResult Checkout(ShoppingCartModel model)
{
	string message = string.Format("Successfully processed {0} item(s).", model.CartItems.Count.ToString());
	return Json(new { Success = true, Message = message });
}

So what you may notice here is there is no visible code converting our JSON object to our ShoppingCartModel object.
This is because, in ASP.NET MVC 3, there's a 'value provider' factory that is doing this for us automatically. This factory attaches to our Controllers' Action Method and converts our JSON object into our ShoppingCartModel. It uses our Action Methods Signature to determine what Type we are expecting and it attempts to convert the input it received (from our POST) into that Type. This all happens inside MVC's binding logic. In MVC 2, you had to write your own 'Value Provider Factory' and enable it so it could run when the data posted to an Action was about to be bound to the type specified on the Actions input parameter(s).

The next thing you'll notice is how I send a JSON object back to the caller. Here I use what's known as an anonymous type, basically defining an object on the fly, and pass this back through the Json method. This method takes in your object and serializes it into JSON format and returns this to the caller.

Now on the UI I can simply access this object as follows inside the success method of my ajax post:

function PostData() {
		$.ajax({
			url: '@Url.Content("~/Store/Checkout")',
			async: false,
			type: "POST",
			data: JSON.stringify(m_ShoppingCart),
			dataType: "json",
			contentType: "application/json; charset=utf-8",
			error: function (jqXHR, textStatus, errorThrown) {
				alert(jqXHR + "-" + textStatus + "-" + errorThrown);
			},
			success: function (data, textStatus, jqXHR) { $("#Results").show(); $("#ResultMessage").html(data.Message); }
		});
}

The returned JSON object comes back as the object named [data] as seen in the success callback above. [data] then maps directly to my anonymous object which has the properties, [Success] and [Message]. So if I wanted to access the message property, I would simply type data.Message;

Conclusion

So there you have it, oh and before I go, just remember the gotchas:

  1. Make sure your parameters on the jQuery Ajax post are set correctly
  2. The automatic JSON binding only works from ASP.NET MVC 3 and up, for MVC 2 you'd have to write your own.
  3. Don't forget that the decimal numbers you send through must be formatted with the correct decimal places before you send it to the Controllers Action Method (the toFixed() method)
  4. And most importantly, YOUR JSON OBJECT PROPERTY NAMES MUST BE EXACTLY THE SAME AS YOUR C# OBJECT NAMES, if they are not, it will not be able to map properly and from what I've seen it is case sensitive.

I really hope this post helps you guys create really usable, responsive and efficient sites/applications and please, let me know your thoughts and comments.

I'd really love to hear what you guys think.

 

The code for this article can be downloaded from here.

Tags: , , , , , ,

Global ASP.NET Hosting Leader - Click Here

Comments (19) -

Madeline Lewis
Madeline Lewis United States
12/20/2012 12:18:02 PM #

Thank you!

Reply

zahid
zahid Portugal
5/7/2013 2:26:42 PM #

excellent post, pretty straight forward

Reply

Pawan Raut
Pawan Raut India
6/12/2013 9:51:20 AM #

Most Helpul.....Thanks lot

Reply

Raja Nandam
Raja Nandam United States
7/10/2013 3:43:28 AM #

Thanks a lot sir ,

i am new to MVC3 Application development . i feel very happy after reading your article . It is very useful to me

it is great content and your explanation is excellent .

Thank you so much for your help

Thank you
Raja Nandam

Reply

Miguel Ribeiro
Miguel Ribeiro United States
10/1/2013 9:29:57 AM #

It's a pleasure Raja, glad it's helped you.

Reply

Juan
Juan Colombia
7/30/2013 6:07:55 PM #

Very nice and simply comment.

Reply

Alani
Alani Sweden
9/3/2013 1:17:24 PM #

Thanks alot.

Reply

sunone
sunone Sri Lanka
9/7/2013 11:48:09 PM #

this is brilliant article. this article's most important part for me  is Json object "m_ShoppingCart" and    "m_ShoppingCart.CartItems.push" method.
this is me and my project
big thanks for you

Reply

chika
chika Sri Lanka
11/5/2013 6:59:40 AM #

grate example, and it saved me lot of time, while i was struggling to send 'heder detal' (List inside a list) list to web method i got this link.i run it in asp.net it worked well.keep it up

Thanks

Reply

murali achar m
murali achar m India
11/27/2013 2:14:05 PM #

thank u very much for giving wonderfull idea about ajax....

Reply

Jigar Sangoi
Jigar Sangoi India
12/2/2013 11:20:00 AM #

Excellent Post. Most Helpul

Reply

siva
siva India
1/29/2014 5:04:44 AM #

very good post... thank you so muc..

Regards,
siva
www.doditsolutions.com

Reply

Danfer Habed L&#243;pez
Danfer Habed López Nicaragua
2/8/2014 12:10:24 AM #

Very good post, I'm looking forward to follow your blog and work.

Cheers,
Danfer.

Reply

Mandy Dee Twitter
Mandy Dee Twitter United States
2/12/2014 6:21:46 AM #

Could I have your approval to tweet this on my twitter?

Reply

Miguel Ribeiro
Miguel Ribeiro United States
3/5/2014 7:22:08 PM #

Hi Mandy,
I'd be really grateful if you did, thank you

Reply

Mandy Dee
Mandy Dee United States
2/14/2014 2:08:33 AM #

Have any arguments about me sharing this on twitter?

Reply

Miguel Ribeiro
Miguel Ribeiro United States
3/5/2014 7:22:48 PM #

Hi,

I'd be really grateful if you did.

Thank You

Reply

hayat
hayat Morocco
3/15/2014 5:36:40 PM #

Thank You

Reply

Elena
Elena Russia
7/29/2014 3:41:56 PM #

Thanks very much

Reply

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

Global ASP.NET Hosting Leader - Click Here