In the previous part I demonstrated how jQuery animations can add some jazz to your web forms. Now let's see one of the most important feature of jQuery that you will probably use in all data driven websites - accessing server data. In the previous articles you used jQuery methods such as $.get() to make a GET request to the server. More powerful feature, however, is to make AJAX calls to ASP.NET Web Services, Page Methods and WCF services. The $.ajax() method of jQuery allows you to access these services. In fact $.get() method you used earlier internally makes use of $.ajax() method but restricts itself only to GET requests. The $.ajax() method provides more control on how the services are called.
The general signature of $.ajax() method is as follows :
$.ajax({
type: get_or_post,
url: url_goes_here,
data: service_parameters_goes_here,
contentType: "application/json; charset=utf-8",
dataType: json_or_xml,
processData: true_or_false;
success: function(results){...},
error: function(err) {...}
})
Though many of the parameters mentioned above (and there are still more!) are optional knowing about them is a good idea so that you can have total control on the AJAX calls being made. A brief description of each of the parameters mentioned above is as follows:
- The type parameter indicates the type of request i.e. GET or POST
- The URL parameter is the URL to the remote service method to be called. A typical URL will be MyService.asmx/MyMethod for Web Services and MyService.svc/MyMethod for WCF services.
- The data parameter allows you pass parameters required by the remote method. Typically data will be a JSON serialized object.
- contentType indicates the type of content being sent on the wire. Most of the times it will be "application/json".
- dataType indicates whether data will be in JSON format or XML format.
- processData indicates whether to add data specified in data parameter to request querystring. For POST requests you will set it to false.
- success is a function that will be invoked after successful completion of the remote call.
- error is a function that will be invoked in case there is any error while calling the remote service.
Example Services
As an example to illustrate how $.ajax() method can be used we will develop two services and a couple of page methods. All of them essentially do a trivial job - converting temperatures from Celsius to Fahrenheit and vice a versa. Our web form will look like this:
The web form allows us to enter temperature value and its unit. Then we can select whether to call ASMX or WCF service or Page Methods. Clicking on Convert converts the temperature value to other unit and displays under Result column.
Creating ASMX Service
Create a new website and add a new Web Service (.asmx) to it. Then create the following methods in the webservice.
[WebMethod]
public decimal ConvertTemperatureSimple(decimal t, string unit)
{
if (unit == "C")
{
t = (t * 1.8m) + 32;
}
else
{
t = (t - 32) / 1.8m;
}
return t;
}
[WebMethod]
public TemperatureData ConvertTemperatureComplex(TemperatureData t)
{
if (t.Unit == "C")
{
t.Value = (t.Value * 1.8m) + 32;
t.Unit = "F";
}
else
{
t.Value = (t.Value - 32) / 1.8m;
t.Unit = "C";
}
return t;
}
The ConvertTemperatureSimple() method has parameters and return values as simple primitive data types. It accepts temperature value to convert and its current unit. Internally depending on whether the unit is "C" or "F" it converts the temperature to another unit. The calculated temperature is then returned.
The ConvertTemperatureComplex() method makes use of complex data types i.e. TemperatureData class. Internal working of ConvertTemperatureComplex() method is similar to the previous method. The TemperatureData class is shown below:
public class TemperatureData
{
public TemperatureData() { }
public TemperatureData(decimal value, string unit, DateTime takenon)
{
this.Value = value;
this.Unit = unit;
this.TakenOn = takenon;
}
public decimal Value { get; set; }
public string Unit { get; set; }
public DateTime TakenOn { get; set; }
}
The TemperatureData class essentially stores temperature value, unit and date on which the temperature is recorded (though we don't use this property for any meaningful purpose in this example).
Creating WCF Service
We will also add a WCF service to our website that is equivalent in terms of functionality to the web service we just developed. The following code shows WCF service methods.
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped)]
decimal ConvertTemperatureSimple(decimal t,string unit);
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
TemperatureData ConvertTemperatureComplex(TemperatureData t);
}
[DataContract]
public class TemperatureData
{
public TemperatureData() { }
public TemperatureData(decimal value, string unit,DateTime takenon)
{
this.Value = value;
this.Unit=unit;
this.TakenOn = takenon;
}
[DataMember]
public decimal Value { get; set; }
[DataMember]
public string Unit { get; set; }
[DataMember]
public DateTime TakenOn { get; set; }
}
public class Service : IService
{
public decimal ConvertTemperatureSimple(decimal t, string unit)
{
if (unit == "C")
{
t = (t * 1.8m) + 32;
}
else
{
t = (t - 32)/1.8m;
}
return t;
}
public TemperatureData ConvertTemperatureComplex(TemperatureData t)
{
if (t.Unit == "C")
{
t.Value = (t.Value * 1.8m) + 32;
t.Unit = "F";
}
else
{
t.Value = (t.Value - 32) / 1.8m;
t.Unit = "C";
}
return t;
}
}
I won't go into the technical details of creating a WCF service here. You will find many articles on this website that teach you how do that. Notice the lines marked in bold letters. That's an important piece of code for us. The [WebInvoke] attribute from System.ServiceModel.Web namespace marks a service method as REST callable. REST (REpresentational State Transfer) is a programming model for getting information from a web accessible URL (typically to a web page or service). In jargon free terms REST means retrieving data from a web accessible resource (e.g. a web page) using HTTP GET or POST methods.
The WebInvoke attribute has several properties as described below:
- Method : Indicates HTTP verb GET or POST that can invoke this method.
- RequestFormat and ResponseFormat : Indicate the format of request and response respectively. Possible values are JSON and XML.
- BodyStyle : Indicates whether to wrap data within system defined XML elements or not. Possible options are Bare, Wrapped, WrappedRequest and WrappedResponse. For methods with only one parameter the default of Bare works fine. However, if your service method is taking more than one parameters then you should make it Wrapped.
Creating Page Methods
Creating Page Methods is very similar to creating Web Service methods. The only difference is that they reside in the code behind file of the web form and are static. Have a look below:
[WebMethod]
public static decimal ConvertTemperatureSimple(decimal t, string unit)
{
...
}
[WebMethod]
public static TemperatureData ConvertTemperatureComplex(TemperatureData t)
{
...
}
Calling Service Methods
The jQuery code that calls the service methods is inside the click event handler of Convert button. The event handler is wired in the ready event as shown below:
$(document).ready(function() {
$("#btnConvert").click(OnConvert);
});
The OnConvert function that acts as the click event handler of Convert button contains switch-case statement that checks the value selected in dropdown Select2.
function OnConvert(event) {
var url = "";
var data="";
var successHandler=null;
var temp = $("#TextBox1").val();
var unit=$("#Select1").val();
switch ($("#Select2").val()) {
case "ASMX1":
url = "WebService.asmx/ConvertTemperatureSimple";
data = '{"t":"' + temp + '","unit":"' + unit + '"}';
successHandler=function(results) {
$("#lblResult").text(results.d);
$("#lblTakenOn").text("");
}
break;
case "ASMX2":
url = "WebService.asmx/ConvertTemperatureComplex";
data = '{t:{"Value":"' + temp + '","Unit":"' + unit +
'","TakenOn":"12/10/2010"}}';
successHandler = function(results) {
$("#lblResult").text(results.d.Value + " " + results.d.Unit);
$("#lblTakenOn").text("on " + ToJSDate(results.d.TakenOn));
}
break;
case "WCF1":
url = "Service.svc/ConvertTemperatureSimple";
data = '{"t":"' + temp + '","unit":"' + unit + '"}';
successHandler=function(results) {
$("#lblResult").text(results.ConvertTemperatureSimpleResult);
$("#lblTakenOn").text("");
}
break;
case "WCF2":
url = "Service.svc/ConvertTemperatureComplex";
data = '{"Value":"' + temp + '","Unit":"' + unit + '","TakenOn":"' +
ToWCFDate("12/10/1960") + '"}';
successHandler=function(results) {
$("#lblResult").text(results.Value + " " + results.Unit);
$("#lblTakenOn").text("on " + ToJSDate(results.TakenOn));
}
break;
case "PM1":
url = "Default.aspx/ConvertTemperatureSimple";
data = '{"t":"' + temp + '","unit":"' + unit + '"}';
successHandler = function(results) {
$("#lblResult").text(results.d);
$("#lblTakenOn").text("");
}
break;
case "PM2":
url = "Default.aspx/ConvertTemperatureComplex";
data = '{t:{"Value":"' + temp + '","Unit":"' + unit +
'","TakenOn":"12/10/2010"}}';
successHandler = function(results) {
$("#lblResult").text(results.d.Value + " " + results.d.Unit);
$("#lblTakenOn").text("on " + ToJSDate(results.d.TakenOn));
}
break;
}
$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: successHandler,
error: function(err) {
alert(err.status + " - " + err.statusText);
}
})
}
Each "case" essentially forms the REST endpoint to be invoked. As described earlier the general format of URL is
<service_asmx_or_svc_file_name>/<method_name>
The method parameters need to be in JSON format. JSON format is basically a key-value format wherein keys and values are enclosed in double quotes and are separated by :. Multiple key-value pairs are separated by a comma (,). The entire set of key-value pairs is enclosed within { and }. For primitive data types key name must be same as the method parameter name and for object types key name must match the property name. Notice the differences between the JSON strings for ASMX and WCF.
The success handler receives the return value of the service method as a parameter (result). For Web Services the return value is made available as a member of implicit object d. For WCF services you can access the return value directly.
JSON format takes care of serializing data types such as string, integers and boolean. However, there is no way to represent Date data type in JSON. That means dates are treated as plain strings. To avoid any confusion ASMX and WCF services serialize dates in a special format. The format is as follows:
\/Date(no_of_mlilliseconds_since_1_Jan_1970)\/
To take care of this format we will create two helper methods ToWCFDate() and ToJSDate().
function ToJSDate(value) {
var pattern = /Date\(([^)]+)\)/;
var results = pattern.exec(value);
var dt = new Date(parseFloat(results[1]));
return (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();
}
function ToWCFDate(value) {
var dtArr = value.split("/");
var dt = new Date(dtArr[2], --dtArr[0], dtArr[1]);
var date = '\/Date(' + dt.getTime() + '+0000)\/';
return date;
}
The ToJSDate() function accepts date in the special format as mentioned above and then converts it into mm/dd/yyyy format. It does so by first matching a regular expression and then converting the millisecond value back into a JavaScript date. Similarly ToWCFDate() function converts specified date from mm/dd/yyyy format to WCF service specific format using getTime() function. The getTime() function returns number of milliseconds since 1 January 1970 and that specific date instance. The time zone information is also appended (+0000 or whatever time zone you want say +0530 for IST)
That's it! Run the web form and try converting a few temperature values using ASMX and WCF service methods.