Friday, September 11, 2015

jQuery AJAX In ASP.NET Razor Web Pages

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);
}