Thursday, May 6, 2010

Calling PHP SOAP Server from C#/.NET Client

So SOAP Web Services is a pretty mature technology now, and performing cross-language service calls should be a breeze, right? In utopia, yes - unfortunately we live in the real world. I did finally make it work but it took me a day and a half, so I thought I'd share my experience here.
There are other guides on the net that focuse on NuSOAP, like this one on Sanity Free Coding, but I prefer to use the stock SOAP library in PHP5 so that's the focus for this guide.


The server: WSDL or non-WSDL-mode?

As of now, the PHP5 SOAP extension does not support auto-generating WSDL as the .Net Framework does. But it does support a non-WSDL mode. That creates an issue though, because Visual Studio relies on WSDL files in order to create the client reference as illustrated below.



To make a long story short, I never got non-WSDL mode to work with .NET although I got pretty close, but then I relied on a dummy WSDL on the development computer just to fool Visual Studio into creating the proper service reference. So I needed the WSDL anyway.


If you're not proficient in WSDL-hacking, do what I did: Create a dummy .NET Web Service project with the method shells as intended for the PHP server. In my example here I'm going to create a simple service with one input and one output variable. The method TimesTwo doubles the integer input value and returns the result. In C# the code looks like this:


[WebService(Namespace = "http://myservice.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    public class SomeService : System.Web.Services.WebService
    {
        [WebMethod]
        public int TimesTwo(int value)
        {
            return 0;
        }
    }
This I implemented in my dummy web service project. I have not implemented the business logic in the body - I only do as much as I need in order for the service to compile. I then get the WSDL by launching the app and adding ?WSDL to the URL. In my case, this became http://localhost:7048/SomeService.asmx?WSDL. Save this file for later use at the PHP server.

The Client

Now over to the client. I'm creating a simple Windows Forms app which takes an input, allows the user to trigger the web service call and displays the output. See screenshot below.


Add a service reference to the solution by pointing to the local dummy project. It would be a good idea to also test the client in this phase and see that the dummy server returns the value zero. The client is really the easy part, so I'm moving over to the juice.

The PHP server

I'm not going into detail on installation and configuration. Consult the PHP SOAP manual for this.Now make sure you upload the WSDL file to the server. Let's assume you call it "SomeService.wsdl" and place it in the same folder as the server file itself. This is the code you need to provide the desired functionality:


<?php
 ini_set("soap.wsdl_cache_enabled", "0");
 $server = new SoapServer("SomeService.wsdl");
 $server->addFunction("TimesTwo");
 $server->handle();
 
 function TimesTwo($valueObj) 
 {   
  // Unwrap input data
  $valueArr = get_object_vars($valueObj);
  $value = $valueArr["value"];  
  // Perform business operations
  $result = $value * 2; 
  // Wrap and return the result
  return array("TimesTwoResult" => $result);
 }  
?>

Now the tricky part was understanding how .NET wrapped the input data and how it expected the return data to be wrapped. Some Googling lead me to the answer on the return value. A bit of trial and error and with aid of the serialize() function, I figured out the input data. Note that the string literal ("TimesTwoResult") on the return line needs to to match the result specification in the WSDL. Similar, the "value" property in the input object is the name of the first input parameter.
Now that's all there is to it!

Download source code.








9 comments:

  1. i'm noob in c#, maybe you ask me? I'm recived array data in the soap envelop, but string s1 = client.TimesTwo("foo");

    string input = InputTextBox.Text;
    ResultTextBox.Text = s1;

    - -------this code snipet return just first element of soap complex type array-----
    How can i fetch all elements? for example 10 items?
    Thak you

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Sorry for late answer. How is your PHP code implemented? Are you saying you are returning an array of String's? If so, try changing the method signatur in C# to "String[]" or "List<String<"

      Delete