Thursday, February 07, 2008

Implementing WCF Service Part 4 - POX/REST

This post will focus on implementing POX (Plain Old XML) and REST (Representational State Transfer) in WCF. In other words this is an implementation of the old HTTP XML Post. I spent not several hours but several days to figure out the solution. As all of you know making HTTP GET is easy in WCF but HTTP Post (without using SOAP) is a different story. I researched all around the web but nowhere found a simple example for this option, yes not even on Microsoft site. Many people just post their opinion in few lines and then you keep guessing, and if you are new to this then keep doing trial and error method for hours and days provided you keep your patience on. Once you will find the solution, it will look so easy.

 
So, In order to make this happen, we will have to make changes at following places

1.       Web.Config – Both at Service and Client side for new binding
2.       ICustomerService.vb
3.       CustomerService.svc.vb
4.       CustomerWcfServiceTest.aspx – For adding new HTML form fields and JavaScript functions
 
So let’s see step by step what we need to make change in each file.
Web.Config (WCF Service) – Add the following in respective sections
Web.Config (WCF Client or WebSite) : Once you made the above change in your service Config file, you can update your service reference in client application or web site.When you click on update Web/Service references, It will automatically add the following sections in respective area, if not make sure that you add it in client web site Web.config file.
<customBinding>
    <binding name="WebHttpBinding_ICustomerService">
     <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
      messageVersion="Soap12" writeEncoding="utf-8">
      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
       maxBytesPerRead="4096" maxNameTableCharCount="16384" />
     </textMessageEncoding>
    </binding>
   </customBinding>
 
<endpoint address="http://vishwa/ExampleService/CustomerService.svc/pox"
    binding="customBinding" bindingConfiguration="WebHttpBinding_ICustomerService"
    contract="CustomerWcfService.ICustomerService" name="WebHttpBinding_ICustomerService" />
ICustomerService.vb:  First we need to modify the Service Contract, you can use the existing methods for implementing HTTP GET, but for HTTP POST, you will need separate interface. Replace the existing code of this file with following code
Imports System.ServiceModel.Web 
 
Namespace Example.WcfService    
 
<ServiceContract(Name:="ICustomerService", NameSpace:="http://wcfservices.vishwamohan.net")> _    
Public Interface ICustomerService         
'Implementing HTTP GET using WebGet()         
<OperationContract(Name:="GetCustomer"), WebGet()> _        
Function GetCustomer(ByVal ID As Integer) As Customer         
<OperationContract(Name:="GetCustomers"), WebGet()> _       
Function GetCustomers() As List(Of Customer)         
<OperationContract(Name:="AddCustomer")> _        
Function AddCustomer(ByVal CustomerRecord As Customer) As Integer         
<OperationContract(Name:="UpdateCustomer")> _        
Function UpdateCustomer(ByVal CustomerRecord As Customer) As Boolean         
<OperationContract(Name:="DeleteCustomer")> _        
Function DeleteCustomer(ByVal ID As Integer) As Boolean   
 
'Implementing POX/REST - HTTP POST         
<OperationContract(Name:="GetCustomerByPOX"), _            
WebInvoke(Method:="POST", BodyStyle:=WebMessageBodyStyle.Bare, _            
RequestFormat:=WebMessageFormat.Xml, ResponseFormat:=WebMessageFormat.Xml)> _        
Function GetCustomerByPOX(ByVal XMLString As System.Xml.XmlElement) As Customer         
<OperationContract(Name:="GetCustomersByPOX"), WebInvoke(Method:="POST", _                            
BodyStyle:=WebMessageBodyStyle.Bare, _                            
RequestFormat:=WebMessageFormat.Xml, _                            
ResponseFormat:=WebMessageFormat.Xml)> _        
Function GetCustomersByPOX() As List(Of Customer)         
<OperationContract(Name:="AddCustomerByPOX"), WebInvoke(Method:="POST", _                            
BodyStyle:=WebMessageBodyStyle.Bare, _                            
RequestFormat:=WebMessageFormat.Xml, _                            
ResponseFormat:=WebMessageFormat.Xml)> _        
Function AddCustomerByPOX(ByVal RequestXML As System.Xml.XmlElement) As Integer         
<OperationContract(Name:="UpdateCustomerByPOX"), WebInvoke(Method:="POST", _                            
BodyStyle:=WebMessageBodyStyle.Bare, _                            
RequestFormat:=WebMessageFormat.Xml, _                            
ResponseFormat:=WebMessageFormat.Xml)> _        
Function UpdateCustomerByPOX(ByVal RequestXML As System.Xml.XmlElement) As Boolean         
<OperationContract(Name:="DeleteCustomerByPOX"), _           
WebInvoke(Method:="POST", BodyStyle:=WebMessageBodyStyle.Bare, _           
RequestFormat:=WebMessageFormat.Xml, ResponseFormat:=WebMessageFormat.Xml)> _        
Function DeleteCustomerByPOX(ByVal XMLString As System.Xml.XmlElement) As Boolean 
 
End Interface
 
End Namespace
 
CustomerService.svc.vb: Once the service contracts are implemented, now we need these methods to be added to actual service page to accept the request and send the response. Add the following code.
Public Function GetCustomerByPOX(ByVal RequestXML As System.Xml.XmlElement) As Customer Implements ICustomerService.GetCustomerByPOX            Dim custID As Integer = 0             If Not RequestXML.Item("ID") Is Nothing AndAlso IsNumeric(RequestXML.Item("ID").InnerText) Then                custID = CInt(RequestXML.Item("ID").InnerText)            End If             Return ServiceHelper.GetCustomerData(custID)         End Function         Public Function AddCustomerByPOX(ByVal RequestXML As System.Xml.XmlElement) As Integer 
Implements ICustomerService.AddCustomerByPOX            
Dim returnID As Integer = 0            
If Not RequestXML.Item("CustomerRecord") Is Nothing Then                
Dim customerRecord As New Customer                
If Not RequestXML.Item("CustomerRecord").Item("ID") Is Nothing AndAlso _             
   IsNumeric(RequestXML.Item("CustomerRecord").Item("ID").InnerText) Then                    
customerRecord.CustID = CInt(RequestXML.Item("CustomerRecord").Item("ID").InnerText)                
End If                
 
If Not RequestXML.Item("CustomerRecord").Item("Name") Is Nothing Then _                    
    customerRecord.CustName = RequestXML.Item("CustomerRecord").Item("Name").InnerText                
If Not RequestXML.Item("CustomerRecord").Item("DOB") Is Nothing AndAlso _                    
    IsDate(RequestXML.Item("CustomerRecord").Item("DOB").InnerText) Then                    
    customerRecord.CustDOB = CDate(RequestXML.Item("CustomerRecord").Item("DOB").InnerText)                
End If                
 
If Not RequestXML.Item("CustomerRecord").Item("Address") Is Nothing Then _                    
    customerRecord.CustAddress = RequestXML.Item("CustomerRecord").Item("Address").InnerText                 
returnID = ServiceHelper.AddCustomerData(customerRecord)            
End If            
 
Return returnID         
 
End Function         
 
Public Function GetCustomersByPOX() As System.Collections.Generic.List(Of Customer) Implements ICustomerService.GetCustomersByPOX            Return ServiceHelper.GetCustomersData()        End Function         Public Function UpdateCustomerByPOX(ByVal RequestXML As System.Xml.XmlElement) As Boolean Implements ICustomerService.UpdateCustomerByPOX            Dim returnValue As Boolean = False            If Not RequestXML.Item("CustomerRecord") Is Nothing Then                Dim customerRecord As New Customer                If Not RequestXML.Item("CustomerRecord").Item("ID") Is Nothing AndAlso _                    IsNumeric(RequestXML.Item("CustomerRecord").Item("ID").InnerText) Then                    customerRecord.CustID = CInt(RequestXML.Item("CustomerRecord").Item("ID").InnerText)                End If                If Not RequestXML.Item("CustomerRecord").Item("Name") Is Nothing Then _                        customerRecord.CustName = RequestXML.Item("CustomerRecord").Item("Name").InnerText                If Not RequestXML.Item("CustomerRecord").Item("DOB") Is Nothing AndAlso _                    IsDate(RequestXML.Item("CustomerRecord").Item("DOB").InnerText) Then                    customerRecord.CustDOB = CDate(RequestXML.Item("CustomerRecord").Item("DOB").InnerText)                End If                If Not RequestXML.Item("CustomerRecord").Item("Address") Is Nothing Then _                    customerRecord.CustAddress = RequestXML.Item("CustomerRecord").Item("Address").InnerText                 returnValue = ServiceHelper.UpdateCustomerData(customerRecord)             End If            Return returnValue        End Function         Public Function DeleteCustomerByPOX(ByVal RequestXML As System.Xml.XmlElement) As Boolean Implements ICustomerService.DeleteCustomerByPOX            Dim custID As Integer = 0            Dim returnValue As Boolean = False            If Not RequestXML.Item("ID") Is Nothing AndAlso IsNumeric(RequestXML.Item("ID").InnerText) Then                custID = CInt(RequestXML.Item("ID").InnerText)                returnValue = ServiceHelper.DeleteCustomerData(custID)            End If            Return returnValue        End Function
 
Now you are done from service side. Compile the project and deploy to the same place where you did before.
 CustomerWcfServiceTest.aspx: Now I am back to same old page in which I tested earlier posts. This time I will add following HTML code and JavaScript inside body tag. This will allow you use simple XML for transaction without SOAP.
<form id="frmCustomerGet1" method="get" action="http://vishwa/exampleservice/customerservice.svc/pox/GetCustomer">                   
    <table>
    <tr><td colspan="2"><b>Through Client Side HTML Form HTTP GET</b></td></tr>
    <tr><td>Customer ID :</td><td><input name="ID" type="text" value="969225896" /> </td></tr>
     <tr>
        <td colspan="2"><input type="submit" name="btnGetCustomer" value="Get a Customer"/></td>
    </tr>   
    </table>                
</form>
 
<form id="frmCustomerGet2" method="get" action="http://vishwa/exampleservice/customerservice.svc/pox/GetCustomers">
    <div>    
        <input type="submit" name="btnGetAllCustomers" value="Get All Customers"/>
    </div>   
</form>                
 
 <br />
<form id="frmCustomerPost1" method="post" action="http://vishwa/exampleservice/customerservice.svc/pox">
   <table>
    <tr><td colspan="2"><b>Through Client Side Plain Old XML (POX) HTTP POST</b></td></tr>
     <tr><td>Customer ID :</td><td><input name="ID" type="text" value="969225896" /> </td></tr>
    <tr><td>Customer Name :</td><td><input name="Name" type="text" value="Chris Clark" /> </td></tr>
    <tr><td>Customer DOB (yyyy-mm-dd):</td><td><input name="DOB" type="text" value="1980-08-08"/> </td></tr>
    <tr><td>Customer Address :</td><td><input name="Address" type="text" value="unknown"/> </td></tr>
     <tr>
        <td><input type="button" name="btnGetCustomer" value="Get Customer" onclick="GetCustomerByPOX()" /></td>
        <td><input type="button" name="btnAddCustomer" value="Add Customer" onclick="AddCustomerByPOX()" /></td>
    </tr>
     <tr>
        <td><input type="button" name="btnUpdateCustomer" value="Update Customer" onclick="UpdateCustomerByPOX()" /></td>
        <td><input type="button" name="btnDeleteCustomer" value="Delete Customer" onclick="DeleteCustomerByPOX()"/></td>
    </tr>     
    <tr>
        <td colspan="2"><input type="button" name="btnGetAllCustomers" value="Get All Customers" onclick="GetCustomersByPOX()"/></td>
    </tr>    
</table>    
</form>
 
Add Following JavaScript in the page
<script type="text/javascript" language="javascript">
 
function GetCustomerByPOX() 
           {
             var dataText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; 
                 dataText += "<GetCustomer><ID>" + frmCustomerPost1.ID.value + "</ID>"; 
                 dataText += "</GetCustomer>";
 
                 alert(dataText);
 
             urlToPost = "http://vishwa/exampleservice/customerservice.svc/pox/GetCustomerByPOX"
             var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");                  
                 xmlHttp.open("POST", urlToPost,false);               
                 xmlHttp.setRequestHeader("Content-Type", "text/xml");
                 xmlHttp.send(dataText);
                 alert(xmlHttp.responseText);
           }
 
 
        function AddCustomerByPOX() 
           {
             var dataText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; 
                 dataText += "<AddCustomer><CustomerRecord>"; 
                 dataText += "<ID>" + frmCustomerPost1.ID.value + "</ID>"; 
                 dataText += "<Name>" + frmCustomerPost1.Name.value + "</Name>"; 
                 dataText += "<DOB>" + frmCustomerPost1.DOB.value + "</DOB>";                       
                 dataText += "<Address>" + frmCustomerPost1.Address.value + "</Address>";              
                 dataText += "</CustomerRecord></AddCustomer>"; 
 
                 alert(dataText);
             urlToPost = "http://vishwa/exampleservice/customerservice.svc/pox/AddCustomerByPOX"
             var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");                  
                 xmlHttp.open("POST", urlToPost,false);               
                 xmlHttp.setRequestHeader("Content-Type", "text/xml");
                 xmlHttp.send(dataText);
                 alert(xmlHttp.responseText);
           }
 
        function UpdateCustomerByPOX() 
           {
             var dataText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; 
                 dataText += "<UpdateCustomer><CustomerRecord>"; 
                 dataText += "<ID>" + frmCustomerPost1.ID.value + "</ID>"; 
                 dataText += "<Name>" + frmCustomerPost1.Name.value + "</Name>"; 
                 dataText += "<DOB>" + frmCustomerPost1.DOB.value + "</DOB>"; 
                 dataText += "<Address>" + frmCustomerPost1.Address.value + "</Address>";
                 dataText += "</CustomerRecord></UpdateCustomer>"; 
 
                 alert(dataText);
             urlToPost = "http://vishwa/exampleservice/customerservice.svc/pox/UpdateCustomerByPOX"
             var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");                  
                 xmlHttp.open("POST", urlToPost,false);               
                 xmlHttp.setRequestHeader("Content-Type", "text/xml");
                 xmlHttp.send(dataText);
                 alert(xmlHttp.responseText);
           }
 
       function DeleteCustomerByPOX() 
           {
             var dataText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; 
                 dataText += "<DeleteCustomer><ID>" + frmCustomerPost1.ID.value + "</ID>"; 
                 dataText += "</DeleteCustomer>";
 
                 alert(dataText);
 
             urlToPost = "http://vishwa/exampleservice/customerservice.svc/pox/DeleteCustomerByPOX"
             var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");                  
                 xmlHttp.open("POST", urlToPost,false);               
                 xmlHttp.setRequestHeader("Content-Type", "text/xml");
                 xmlHttp.send(dataText);
                 alert(xmlHttp.responseText);
           }
 
 
        function GetCustomersByPOX() 
           {
 
             urlToPost = "http://vishwa/exampleservice/customerservice.svc/pox/GetCustomersByPOX"
             var xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");                  
                 xmlHttp.open("POST", urlToPost,false);               
                 xmlHttp.setRequestHeader("Content-Type", "text/xml");
                 xmlHttp.send(null);
                 alert(xmlHttp.responseText);
           }
 
</script>
So, now you are ready to run this page and click the button you like by filling appropriate data. You should be able to see the transaction request and response in form of plain XML as alert.
 


I believe, if you are integrating with an outside customer you will most likely choose WSHttpBinding, BasicHttpBinding or WebHttpBinding in the order of high to lower interoperability  and security preference. Either one you choose performance wise it will be always lower than NetTcpBinding , NetMsmqBinding or NetNamedPipeBinding. However, Net* bindings will only work .NET consumers. If you wish to have best performance then you will have to choose NetNamedPipeBinding but this will require you to have the Service and Consumer on the same machine, if both are on the same network, use NetTcpBinding.
 

No comments: