Using the C# Service Reference to consume a PHP web service

Introduction

This is an article I wrote about using #C/.NET ServiceReference to connect to a PHP webservice when the ServiceReference appears to not correctly read the PHP based webservice response. I later found out that the issue was with the webservice binding style being set to "rpc" and use set to "encoded", when this was switched to binding style "document" and use "literal" the response was deserialized correctly without modifying the ServiceReference behavior. This article shows you how to add some custom code to change the default behavior of the ServiceReference response reading methods when there is no way that you can change something like the binding style of the webservice that you are consuming.

When I first created a simple WindowsForms application using the .NET 4.0 Service Reference to consume a PHP web service I found that the Service Reference auto generated serialization code worked well to send requests to the PHP web service but the auto generated deserialization code did not work for the response received. I would get the error "Object reference not set to an instance of an object," when trying to access fields of an array type. This error pointed out that Objects for XML fields of array types were not successfully created after deserializing the response. My solution was to still use the Service Reference's serialization code to send requests but implement the IClientMessageInspector Interface to view and parse the SOAP XML response. I used the System.Xml.XmlDocument to select elements from the response instead of deserializing it to objects. This method of parsing the XML response directly can apply to consuming any web service in fact, not just one created in PHP.

I used Microsoft Visual C# 2010 Express and it is available free here.

Creating a C# Service Reference


  1. Start a new WindowsForms project
  2. Create a large Textbox with the Mulitline property set to True. The textarea will hold the SOAP response.
  3. Create a Button to start the SOAP request.
  4. In the Solution Explorer, right click the project and select Add Service Reference.
  5. Enter the address of the web service wsdl file. Press Enter and select the service contract.
  6. Press Ok and the auto generated code will be created in Reference.cs. An interface to the service contract is created and a class implementing both this interface and the System.ServiceModel.ClientBase is created in Reference.cs.
  7. Right click the project in Solution Explorer and select Add->Class.
  8. Add the following code to Class1:

IEndpointBehavior and IClientMessageInspector classes

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Dispatcher;
using System.Xml;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

namespace WindowsFormsApplication1
{
    public class MyInspector : IClientMessageInspector
    {
        private string message;
        public string getMessage() { return message; }

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            //save the SOAP response
            message = reply.ToString();
            return;

            /*It is also possible that the web service is always returning a message format which causes an exception. You can replace the message here with a dummy before returning to avoid the exception. See http://blogs.msdn.com/b/kaevans/archive/2008/01/08/modify-message-content-with-wcf.aspx to modify the message in that case.*/
          
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            return null;
        }
    }

    public class MyBehavior : IEndpointBehavior
    {
        private MyInspector inspector;
        public MyInspector getMyInspector() { return inspector; }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            //no-op
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            inspector = new MyInspector();
            clientRuntime.MessageInspectors.Add(inspector);
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            //no-op
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            //no-op
        }
    }
}

10. Double click the button to enter code for the click event:

(do not take this code word for word since you will have a different service contract name and different methods available depending on the wsdl)

private void button1_Click(object sender, EventArgs e)
        {
            //create an instance of the auto generated class which defines the service contract
            ServiceReference1.w_w_ryan_team_ws_dream_team2_6phpPortTypeClient s =
                new ServiceReference1.w_w_ryan_team_ws_dream_team2_6phpPortTypeClient();

            //would hold the response if properly deserialized by the auto generated code
            ServiceReference1.ListCitiesResponse res;
            //create a request object
            ServiceReference1.ListCitiesRequest req = new ServiceReference1.ListCitiesRequest("test", "test");
            
            //call one of the methods of the web service
            res = s.ListCities(req);
            //get the raw SOAP xml from the response to ListCities
            string xml_soap = s.getCustomBehavior().getMyInspector().getMessage();
            
            //create an XmlDocument class from the response xml
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml_soap);
            //Select some elements from the response
            XmlNodeList names = xmlDoc.GetElementsByTagName("CityName");
            //view the elements' inner xml in the textbox
            foreach (XmlElement node in names) {
                textBox1.Text += node.InnerXml;
            }
            
            s.Close();
        }

11. Highlight the service contract class from your ServiceReference1 and right click it to select Go To Definition.

12. Make the following changes to the class members and default constructor:

(again the class name will be different depending on the service contract from the wsdl)

Adding MyBehavior to the service contract

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public partial class w_w_ryan_team_ws_dream_team2_6phpPortTypeClient : System.ServiceModel.ClientBase<WindowsFormsApplication1.ServiceReference1.w_w_ryan_team_ws_dream_team2_6phpPortType>, WindowsFormsApplication1.ServiceReference1.w_w_ryan_team_ws_dream_team2_6phpPortType {
	//add member variable to access the raw message through MyBehavior getCustomBehavior()
        private MyBehavior b;
        public MyBehavior getCustomBehavior() { return b; }

        public w_w_ryan_team_ws_dream_team2_6phpPortTypeClient() {
            //create the MyBehavior object
            b = new MyBehavior();
	    //add MyBehavior to the service contract
            this.Endpoint.Behaviors.Add(b);
        }

Note: The changes made in Reference.cs will be erased if ServiceReference1 is updated by right clicking ServiceReference1 in the Solution Explorer and selecting Update. Make sure to save the project before making changes to the Service Reference via the UI.

More by this Author


Comments 2 comments

rajkishor09 profile image

rajkishor09 4 years ago from Bangalore, Karnataka, INDIA

Hi, I am C# developer and your article is great. Keep up this and give us some good article on C# along with reverse engineering. Best of Luck


Bpar 3 years ago

Thanx

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working