The first thing to look at is the key
settings options that are available for AJAX requests:
type
|
This is type of HTTP
Request and accepts a valid HTTP verb. POST is the option illustrated in this
article.
|
url
|
This is the location of
the resource that the request will be made to.
|
data
|
This is the actual data
to be sent as part of the request.
|
contentType
|
This is the content type
of the request you are making. The default is 'application/x-www-form-urlencoded'.
|
dataType
|
This is the type of data
you expect to receive back. Accepted values are text, xml, json,script, html jsonp.
If you do not provide a value, jQuery will examine the MIME type of the
response and base its decision on that.
|
This
information is provided at the jQuery site (http://api.jquery.com/jQuery.ajax/) and what
is detailed above doesn't add to the official documentation. However, most
issues that I have seen relating to jQuery POST requests seem to result from a
lack of understanding about how these settings actually work.
You
can post a range of different data types including form values, arrays,
JavaScript objects and JSON. First, it is important to clarify that JavaScript
objects and JSON are not the same thing. A JavaScript object is a data type
with properties and values. A simple one is represented in JavaScript as
follows:
var car = { Make: 'Audi', Model: 'A4 Avant', Colour: 'Black', Registered: 2013 };
The
syntax is almost identical to the way that you declare an anonymous type in C#
- just lacking a new in
front of the opening brace and replacing the = with
colons. But there are other ways to declare a JavaScript object. Unlike C#,
JavaScript object properties can be strings as well as identifiers:
var car = { "Make": "Audi", "Model": "A4 Avant", "Colour": "Black", "Registered": 2013 };
This
potentially leads to the confusion between JavaScript objects and JSON, which
is a string representation of data. The JSON representation of the car object
is syntactically identical:
{"Make":"Audi","Model":"A4 Avant","Colour":"Black","Registered":2013}
But
JSON is a data interchange format which has a number of rules: the properties
must be enclosed in double quotes being one. For that reason, and to reduce
confusion, I tend to avoid using quotes around property names in JavaScript
objects. Other differences between JavaScript objects and JSON include support
for data types: JSON does not yet have an accepted standard for representing
datetimes, for example.
Whenever
any request is made to an ASP.NET application, the raw content of that request
populates theRequest.InputStream property. You can access this raw data
by using a StreamReader:
string input;
using(var reader = new StreamReader(Request.InputStream)){
input = reader.ReadToEnd();
}
If
the request content is formatted using a standard encoding such as application/x-www-form-urlencoded,
ASP.NET will parse the response for you and populate the Request.Form collection with the name/value pairs
taken from the InputStream as a convenience.
Posting JavaScript Objects
When
the contentType of an AJAX request is set to application/x-www-form-urlencoded,
jQuery will convert the property names and values of a JavaScript object to a
query string and url-encode it. The following example POSTs the car object from
above:
<script>
$(function () {
$("button").click(function () {
var car = { Make: 'Audi', Model: 'A4 Avant', Colour: 'Black', Registered: 2013 };
$.ajax({
type: "POST",
url: "/Receiver",
data: car,
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
});
});
</script>
In
the example above, the contentType has not been set so the default application/x-www-form-urlencodedwill
be used. The dataType option has been set to html,
which states that I am expecting the "Receiver" page to return some
HMTL to me. If all goes well with the request, the returned HTML will be
plugged in to an element with the id of "result". Here's the code for
the Receiver.cshtml page:
@{
string input;
using(var reader = new StreamReader(Request.InputStream)){
input = reader.ReadToEnd();
}
}
<div>
<h3>Input Stream</h3>
@Server.UrlDecode(input)
</div>
<div>
@if(Request.Form.Count > 0){
<h3>Request Form</h3>
foreach(string item in Request.Form){
<div>@item : @Request[item]</div>
}
}
</div>
The
code does two things: it outputs the InputStream to the browser, having
urldecoded it, and it iterates the Request.Form collection, outputting any
name/value pairs it can find there. The result from posting the car object
looks like this:
So
if you want to post a JavaScript object via jQuery, pass the plain object to
the data option,
and leave thecontentType option alone. The default option is
perfect. Then you can access the property values of the object in the Request
collection as if you have posted a form.
Posting JavaScript Arrays
If
you want to post an array via jQuery, you need to do two things. You must
provide a name for the array, and you need to leave the contentType as the
default value of application/x-www-form-urlencoded:
var ids = [1, 2, 3, 4, 5];
$.ajax({
type: "POST",
url: "/Receiver",
data: { myArray: ids },
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
Here's
what the Receiver page returns:
jQuery
uses the name of the array as the key and serialises all array elements with
the same key - although interestingly, jQuery adds square brackets to the key
value: myArray[]. As far as ASP.NET is
concerned, when the request is processed on the server, it is the same as a
radio or checkbox group. ASP.NET populates the Request.Form collection with one
name/value pair - the name being the key that was generated by jQuery and the
value being the array values as a comma-separated string. You can process them
by using string.Split:
@foreach(var item in Request["myArray[]"].Split(new[]{','})){
// do something with item
}
Posting Form values
I
have seen examples of people constructing a JavaScript object out of form
values and then posting the result and I am sure there are occasions when it
makes sense to do just that. However, in the majority of cases, using theserialize() method is sufficient. This takes
the values of the specified elements and serializes them to a query string.
Here's a simple form:
<form id="myForm">
<div>
@Html.Label("First Name", "firstname")
@Html.TextBox("firstname", "Mike")
</div>
<div>
@Html.Label("Last Name", "lastname")
@Html.TextBox("lastname", "Brind")
</div>
<div>
@Html.Label("Gender", "gender")
@Html.RadioButton("gender", "male", true) Male
@Html.RadioButton("gender", "female") Female
</div>
</form>
Notice
that there is no submit button within the form tags. The button lies outside,
so it won't submit the form by default. This is all that's needed in the button
click event handler:
$.ajax({
type: "POST",
url: "/Receiver",
data: $('#myForm').serialize(),
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
The
serialize command is called on the form element as a whole, which means that
all form fields will be included in the request, and again, the contentType is
not specified so it falls back to the default application/x-www-form-urlencoded.
The result is as below, and is identical if a JavaScript object had been posted
with the same properties and values.
Posting JSON
As
I touched on earlier, there is nothing magical about JSON. It is just a data
interchange format where values are converted to a string. The format has
certain rules that must be followed, and these rules make is easy to create
parsers. Why would you use JSON? Well, for one thing, it is much easier to
represent a complex object in JSON and to let parsers unpick it on the server.
If you posted a complex object using the application/x-www-form-urlencoded format, you would have to parse the
response yourself to reconstruct the object on the server. ASP.NET includes
some API's for deserialising JSON to objects on the server, including the Json
helper for Web Pages.
How
do you create JSON? Most browsers these days include the JSON.stringify method, so that is the recommended way:
var car = { Make: 'Audi', Model: 'A4 Avant', Colour: 'Black', Registered: 2013 };
$.ajax({
type: "POST",
url: "/Receiver",
data: JSON.stringify(car),
contentType: "application/json",
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
On
this occasion, the contentType is set to application/json.
The dataType is still set to html,
since that is the data type of the response I am expecting from the server. And
this time, the response shows that the Rsequest.InputStream is the only place
where I can get at the posted data on the server.
If
I am happy to work with dynamic objects, I can use the dynamic version of Json.Decode on the server to convert the JSON to
something I can work with in C#:
@{
string json;
using(var reader = new StreamReader(Request.InputStream)){
json = reader.ReadToEnd();
}
var car = Json.Decode(json);
}
Or
if I prefer to work with strongly typed objects, I can use the version that
takes a type parameter:
@{
string json;
using(var reader = new StreamReader(Request.InputStream)){
json = reader.ReadToEnd();
}
var car = Json.Decode<Car>(json);
}