You can find a lot of posts on the web on the subject, why writing another one then? Simply because despite all the documentation I’ve already found on the web, while trying to put it in action I’ve spent a couple of hours in making that work, so I’ll use this post to recap all the action I made as a reference guide for my future implementations.

The scenario is: we have a WCF service that is able to accept call to methods with complex parameters (like Messages object in a simple web chat project) and we want to be able to use those methods with jQuery with the minimal impact on the service implementation.

WCF Service setup

We consider a very simple implementation, you don’t have to use any ‘strange’ or unusual attribute here:

The message class we are sending back and forth:

/// <summary>
/// a single message in a conversation
/// </summary>
[DataContract]
public class Message : Entity<int>
{
    /// <summary>
    /// the nickname of who is sending the message
    /// </summary>
    [DataMember]
    public virtual string Sender { get; set; }
 
    /// <summary>
    /// this will be assigned when the object get persisted into the storage, not before
    /// once assigned it's likely to not be modified again
    /// </summary>
    [DataMember]
    public virtual DateTime Timestamp { get; set; }
 
    [DataMember]
    public virtual string Text { get; set; }
}

A test service implementation

/// <summary>
/// service used to serialize/desierialize the data in JSon for JQuery
/// </summary>
[ServiceContract]
public interface IChatService
{
    /// <summary>
    /// return a simple message
    /// </summary>
    /// <returns></returns>
    [OperationContract]
    Message TestMessage();
 
    /// <summary>
    /// a test function that takes a message and returns it modified
    /// </summary>
    /// <param name="msg"></param>
    /// <returns></returns>
    [OperationContract]
    Message TestMessageModify(Message msg);
 
    /// <summary>
    /// a test function that get multiple parameters and return a message
    /// modified
    /// </summary>
    /// <param name="msg"></param>
    /// <param name="id"></param>
    /// <returns></returns>
    [OperationContract]
    Message TestMessageModifyWithSuppliedContent(Message msg, int id);
}
[AspNetCompatibilityRequirements( RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed )]
public class ChatService : IChatService
{
    public Message TestMessage()
    {
        return new Message() { Id = 1, Sender = "Test", Text = "1 - Text message.", Timestamp = DateTime.Now };
    }
 
    public Message TestMessageModify(Message msg)
    {
        msg.Text = "2 - Modified message";
        msg.Timestamp = DateTime.Now;
        return msg;
    }
 
    public Message TestMessageModifyWithSuppliedContent(Message msg, int id)
    {
        msg.Text = "3 - Second parameter passed: " + id;
        msg.Timestamp = DateTime.Now;
        return msg;
    }
}

The only thing needed is the ‘AspNetCompatibilityRequirements’ attribute.

All the magic is done in the WCF configuration section:

   1: <system.serviceModel>
   2:     <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
   3:     <services>
   4:         <service name="RemoteAssistance.Services.Impl.ChatService"
   5:                  behaviorConfiguration="DebugJSonBehavior">
   6:             <endpoint address="" 
   7:                       binding="webHttpBinding" 
   8:                       behaviorConfiguration="DebugJSonBehavior"
   9:                       contract="RemoteAssistance.Services.IChatService">
  10:                 <identity>
  11:                     <dns value="localhost" />
  12:                 </identity>
  13:             </endpoint>
  14:             <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  15:         </service>
  16:     </services>
  17:     <behaviors>
  18:         <endpointBehaviors>
  19:             <behavior name="DebugJSonBehavior">
  20:                 <enableWebScript />
  21:             </behavior>
  22:         </endpointBehaviors>
  23:         <serviceBehaviors>
  24:             <behavior name="DebugJSonBehavior">
  25:                 <serviceMetadata httpGetEnabled="true" />
  26:                 <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" />
  27:             </behavior>
  28:         </serviceBehaviors>
  29:     </behaviors>
  30: </system.serviceModel>

To activate JSON serialization and deserialization for an end point we have to supply some information:

  • in line 7 we set the binding to ‘webHttpBinding’, this is the main actor the inject the JSON DataContract Serializer into the WCF stack.
  • in line 8 we set a specific endpoint behavior for this end point
  • lines 18-22 enable the endpoint behavior that makes it possible to consume the service from ASP.NET AJAX web pages.

Consuming the web service is the real ‘tricky’ point, calling a function without parameter is extremely easy, you can use code like this:

   1: $(document).ready(function() {
   2:     $.ajax({
   3:         type: "POST",
   4:         url: "http://localhost:58829/ChatService.svc/TestMessage",
   5:         data: "{}",
   6:         contentType: "application/json; charset=utf-8",
   7:         dataType: "json",
   8:         success: function(data) {
   9:             //debugger;
  10:             alert(data.d.Text);
  11:         },
  12:         error: function(XMLHttpRequest, textStatus, errorThrown) {
  13:                 debugger;
  14:            alert("Error Occured!");
  15:         }
  16:     });
  17: });

Things get a bit ‘complicated’ when you actually have to pass parameters (special tanks here go to my fellow Alkampfer for helping me find the solution). To pass a parameter successfully you have to supply as the data argument (line 5) a string that is the JSON representation of a JS object which properties matches the parameter name of the function you are calling, the parameters’ values are the actual values that will be sent to the server. If the parameter itself is a complex object (that is a class) you need to supply the JSON representation of the object, Let’s use an example to clarify:

if we want to call the TestMessageModify(Message msg) function we need to supply ad data parameter a JS object made like this:

   1: var msg2 = { "msg": { "Id": "2", "Sender": "Webpage", "Text": "Sended Text"} };

We’re defining an object that contains a ‘msg’ property (same name of the function parameter) which in turn contains another object (defined by the same properties that our Message object have, to allow JSON deserialization to work). What we need to pass to the ‘data’ argument of the function call is the string representation of object:

'{"msg":{"Id":"2","Sender":"Webpage","Text":"Sended Text"}}'

To have the JSON representation of JavaScript object we can use an external library like this one: http://www.JSON.org/json2.js

So, to call TestMessageModify(Message msg) and TestMessageModifyWithSuppliedContent(Message msg, int id), we can use the following code:

var msg2 = { "msg": { "Id": "2", "Sender": "Webpage", "Text": "Sended Text"} };
 
$(document).ready(function() {
    $.ajax({
        type: "POST",
        url: "http://localhost:58829/ChatService.svc/TestMessageModify",
        data: JSON.stringify(msg2),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(data) {
            alert(data.d.Text);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            debugger;
            alert("Error Occured!");
        }
    });
});
 
var msg3 = { "msg": { "Id": "2", "Sender": "Webpage", "Text": "Sended Text"}, "id" : "1" };
 
$(document).ready(function() {
    $.ajax({
        type: "POST",
        url: "http://localhost:58829/ChatService.svc/TestMessageModifyWithSuppliedContent",
        data: JSON.stringify(msg3),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(data) {
            alert(data.d.Text);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            debugger;
            alert("Error Occured!");
        }
    });
});

It ended up being a quite long post for something that should have been much more simple.

Related Content