Udostępnij za pośrednictwem


Using WebHttpBinding & JSON Support in WCF

I had some cool time working with WCF new features shipped with .net 3.5, one of the most ineteresting and useful features is the JSON support and how easy you can enable the JSON support for your current WCF services by changing the configurations.

Why do we need JSON support?

Well, JSON (JavaScript Object Notation) is very suitable for building ajax based applications for some reasons.

  1. The other popular alternative (XML) requires parsing code in the client side to extract the data from the document, which is not the best thing to do using Javascript.
  2. XML documents size are relatively bigger than JSON documents containing the same data

To get more info about JSON check here and here

What WCF offers to support JSON

All what you need is doing two things

  1. Create an end point that uses the new binding (WebHttpBinding)
  2. Configure the end point behavior and enabling the WebScript (which will generate a javascript proxy for your service contract and data contracts)

This is a sample configuration

 <system.serviceModel>
     <behaviors>
       <endpointBehaviors>
         <behavior name="ChatAspNetAjaxBehavior">
           <enableWebScript/>
         </behavior>
       </endpointBehaviors>
     </behaviors>
     <services>
       <service name="ChatService">
         <endpoint address="" behaviorConfiguration="ChatAspNetAjaxBehavior" 
                 binding="webHttpBinding" name="ajaxEndpoint" contract="IChat"/>
       </service>
     </services>
   </system.serviceModel>

As you can see the endpiont configuration is using the Binding "webHttpBinding" and it is also configured in the behavior tag to enable web script.

Now the WCF runtime will generate a javascript proxy containing all services and data contracts as javascript classes, we will see an example shortly.

Example: Building a Chat Application using WCF & JSON support

It is time for more detailed example, in this example we will develop a small web based chat application using javascript as a client language and WCF as a server side.

The chat application will be able to do these functionalities

  1. Register users
  2. User logins
  3. Retrieve users list
  4. Check new messages
  5. Send message
  6. Log out

So let's write down the contract for this service (we will ignore good design practices for the sake of simplicity and will make one service that does both the user management and chatting)

 [ServiceContract(Namespace = "")]
 public interface IChat
 {
     [OperationContract]
     User Register(string username, string password);
  
     [OperationContract]
     User Login(string username, string password);
  
     [OperationContract]
     bool Call(Guid fromUserId, string toUsername, string message);
  
     [OperationContract]
     Message[] GetMyMessages(Guid userId);
  
     [OperationContract]
     bool SignOut(Guid userId);
  
     [OperationContract]
     User[] GetUsers();
 }

 

This is basically how the service contract looks like, we are still missing two data contracts User & Message and here they are.

 [DataContract]
 public class Message
 {
     [DataMember]
     public Guid ID { get; set; }
     [DataMember]
     public string Body { get; set; }
     [DataMember]
     public DateTimeOffset Date { get; set; }
     [DataMember]
     public string FromUsername { get; set; }
  
     public Message()
     {
         ID = Guid.NewGuid();
         Date = DateTimeOffset.Now;
     }
 }
  
 [DataContract]
 public class User
 {
     [DataMember]
     public Guid ID { get; set; }
  
     [DataMember]
     public string Username { get; set; }
  
     public string Password { get; set; }
  
     public User()
     {
         ID = Guid.NewGuid();
     }
 }

The implementation of the service is not very important and the code is attached with the blog entry, basically it saves all the messages and users in static collections, in real life cases the implementation will be different most probably a database is going to be used.

Now let's start building the client side of the chat application.

We will be hosting the chat service on IIS so we will create a .svc file chat.svc which will contain one line

 <%@ ServiceHost Language="C#" Debug="true" Service="ChatService" CodeBehind="~/App_Code/Chat.cs" %>

And this will be the address of our service.

and then we will place the configuration of the service in web.config

 1: <system.serviceModel>
 2:   <behaviors>
 3:     <endpointBehaviors>
 4:       <behavior name="ChatAspNetAjaxBehavior">
 5:         <enableWebScript/>
 6:       </behavior>
 7:     </endpointBehaviors>
 8:   </behaviors>
 9:   <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
 10:   <services>
 11:     <service name="ChatService">
 12:       <endpoint address="" behaviorConfiguration="ChatAspNetAjaxBehavior" 
 13:         binding="webHttpBinding" name="ajaxEndpoint" contract="IChat"/>
 14:     </service>
 15:   </services>
 16: </system.serviceModel>

The configuration is similar to the above one, except for the line #9 which is the serviceHostEnvironment, all what you need to know about this attribute that enabling it will make asp.net treats the WCF service as regular asmx service, for more details about different modes of running WCF services with ASP.net check this article

We will now create two views

  1. Login View
  2. Chat View

The login view is basically like this

Two text boxes (txtUsername, txtPassword) and two input type buttons for register and login actions

The Chat View will look like this

Which contains two more textboxes (txtToUsername, txtMessage) and a select control containing the current users (slGuests) and a panel that will show the message history (pnlMessageHistory)

May be it is not the best user interface but it will do the basic things.

now we will need to add the ScriptManager to the page and add a reference to the wcf service

 1: <asp:scriptmanager id="scrptMgr" runat="server">
 2:     <services>
 3:         <asp:servicereference path="~/Chat.svc" />
 4:     </services>
 5: </asp:scriptmanager>

This will add a script element with a source pointing to this url https://[domain]/Chat.svc/js which will download the javascript proxy code.

We will now do some initialization codes let's see how it looks like...

 1: var user;
 2: var chatService = new IChat();
 3: var guests = new Array();
 4: window.onload = function(){
 5:     var btnRegister = $get("btnRegister");
 6:     $addHandler(btnRegister, "click", doRegister);
 7: }

 

Let's check line #2, this is the neat part of using WebScript, the IChat class is the generated proxy object for the IChat service contract, and for this application we need only one object so the object is created and initialized globaly.

Now let's write the register function

 1: function doRegister()
 2: {
 3:     var username = $get("<%=txtUsername.ClientID %>").value;
 4:     var password = $get("<%=txtPassword.ClientID %>").value;
 5:     chatService.Register(username, password, onRegisterSuccess);
 6: }
 7:  
 8: function onRegisterSuccess(result)
 9: {
 10:     if(result != null)
 11:     {
 12:         user = result;
 13:         var btnRegister = $get("btnRegister");
 14:         btnRegister.value = "Sign Out";
 15:         $clearHandlers(btnRegister);
 16:         $addHandler(btnRegister, "click", doSignOut);
 17:         var btnLogin = $get("btnLogin");
 18:         btnLogin.style.display = "none";
 19:         $get("<%=txtUsername.ClientID
            %>").disabled = true;
 20:         $get("<%=txtPassword.ClientID
            %>").disabled = true;
 21:         $get("divChat").style.display = "inline";
 22:         setTimeout(doCheckMessages, 1500);
 23:     }
 24:     else{
 25:         alert('Error!');
 26:     }
 27: }

 

the doRegister function is calling the Register method defined in the IChat service contract, you will notice a nice feature that VS intellisence is working nice in this part

All the proxy function will have the same parameters defined in the contract, but because of the nature of the asynchronous call, all the functions will add three extra parameters (onSuccessCallback, onFailureCallback, userContext)

The onSuccess callback is very important, as this where you will get to know the return value (if any), and also where you can say that the server finished working on the request, so in the Register example, in the onRegisterSuccess if the user is created, the Login view is disabled and the Chat view is displayed, also an important part is a timer configured for polling the server for new messages, and the timer is called once every 1.5 seconds, and the result parameter to the onRegisterSuccess function is the return value of the Register method on the IChat service, which in our case it is either a User object or null, if the user object is returned it is saved to the global variable user.

Now let's see when we send a new message to a certain user

 1: function doSend()
 2: {
 3:     var pnlHistory = $get("<%=pnlMessageHistory.ClientID %>");
 4:     var txtMessage = $get("<%=txtMessage.ClientID %>");
 5:     var txtTo = $get("<%=txtToUsername.ClientID %>");
 6:     pnlHistory.innerHTML += "<b>" + user.Username + " says:</b><br>" + txtMessage.value + "<br>";
 7:     chatService.Call(user.ID, txtTo.value, txtMessage.value, onSendSuccess);
 8: }
 9:  
 10: function onSendSuccess(result)
 11: {
 12:     var txtMessage = $get("<%=txtMessage.ClientID %>");
 13:     txtMessage.value = "";
 14: }

The send message function will call the Call function on the service, and also writes down the message details on the message history panel.

The callback of this function is simply clears the message textbox.

Now when you send a message to someone else, the message is temporarily saved on server till the recipient user queries the server for his messages, this logic is done through a client side timer.

 1: function doCheckMessages()
 2: {
 3:     chatService.GetMyMessages(user.ID, onCheckMessagesSuccess, onCheckMessagesFail);
 4:     chatService.GetUsers(onGetUsersSuccess);
 5: }
 6:  
 7: function onCheckMessagesSuccess(result)
 8: {
 9:     if(result == null)
 10:         return;
 11:     var pnlHistory = $get("<%=pnlMessageHistory.ClientID %>");
 12:     for(var i=0; i<result.length; i++)
 13:     {
 14:        pnlHistory.innerHTML += "<b>" + result[ i].FromUsername + " says:</b><br>" + result[ i].Body + "<br>"; 
 15:     }
 16:     setTimeout(doCheckMessages, 1500);
 17: }
 18:  
 19: function onCheckMessagesFail(err)
 20: {
 21:     alert(err.message);
 22:     setTimeout(doCheckMessages, 1500);
 23: }

 

The doCheckMessages function does two things, first it checks for the new messages from server, and the other thing is checking for new users in the chat.

the onCheckMessagesSuccess is reading all the messages in the result array of messages, and print them out on the message history with proper format, and then it initialzes the timer again.

This is typically how the chat will look like

Summary

In today's entry I was giving a small overview of one of the new features introduced in WCF 2.0 (.net 3.5) which is WebScript and JSON support, this feature allowed us to create a rich web based application using only Javascript as a client code, we also developed a simple Chatting application to prove the concept and the code is attached with this post for further information and details.

Have a nice day.

 

Technorati Tags: WCF,C#,JSON,Javascript,ASP.net

Chat.zip