#liste_articles {display:block}

SOAP PHP/Perl/C#

vendredi 25 février 2005

 

SOAP PHP/Perl/C#

Le but de cet article c’est de voir comment utiliser SOAP (système de RPC) interfacer des programmes Perl avec des programmes PHP ou C# et ceci pour des services répartis sur le réseau.

Définition d’un service

Avant toute chose, il faut définir le service que l’on veux pouvoir accéder. Dans notre cas, nous allons définir la classe SimpleService avec 2 méthodes (c’est du pseudo code) :

class SimpleService {
  string SayHello(string name)
  int    GetNumber(string number)
}

La première chose à faire pour simplifier les choses et pour permettre à n’importe qui d’accéder à notre service SOAP, c’est de décrire cette classe dans le format WSDL. C’est la partie la plus difficile (enfin selon moi). Il y a bien des outils pour rendre cela plus simple mais jusqu’à présent cela ne m’a que compliqué les choses lorsque l’on veux implémenter le service dans n’importe quel langage.

<?xml version="1.0"?>
  <definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:si="http://soapinterop.org/xsd" xmlns:tns="http://localhost/Simple" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://localhost/Simple">


  <message name="SayHelloRequest">
    <part name="name" type="xsd:string" />
  </message>
  <message name="SayHelloResponse">
    <part name="return" type="xsd:string" />
  </message>
  <message name="GetNumberRequest">
    <part name="auction_id" type="xsd:string" />
  </message>
  <message name="GetNumberResponse">
    <part name="return" type="xsd:int" />
  </message>

  <portType name="SimpleServicePortType">
    <operation name="SayHello">
      <input message="tns:SayHelloRequest"/>
      <output message="tns:SayHelloResponse"/>
    </operation><operation name="GetNumber">
      <input message="tns:GetNumberRequest"/>
      <output message="tns:GetNumberResponse"/>
    </operation>
  </portType>

  <binding name="SimpleServiceBinding" type="tns:SimpleServicePortType">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="SayHello"><soap:operation soapAction="http://localhost/Simple#SayHello" style="rpc"/>
        <input>
          <soap:body use="encoded" namespace="http://localhost/Simple" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
        </input>
        <output>
          <soap:body use="encoded" namespace="http://localhost/Simple" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
        </output>
      </operation>
      <operation name="GetNumber">
        <soap:operation soapAction="http://localhost/Simple#GetNumber" style="rpc"/>
        <input>
          <soap:body use="encoded" namespace="http://localhost/Simple" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
        </input>
      <output>
        <soap:body use="encoded" namespace="http://localhost/Simple" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
    </operation>
  </binding>

  <service name="SimpleService">
    <port name="SimpleServicePort" binding="tns:SimpleServiceBinding">
      <soap:address location="http://localhost/SimpleService.php"/>
    </port>
  </service>
</definitions>

Notez toutefois que le fichier WSDL n’est pas forcément indispensable. Il est indispensable si vous voulez vous simplifier les choses lorsque vous utilisez SOAP dans des langages comme C# ou Java. Pour faire des appels de PHP à Perl ou vice versa on peut très bien s’en passer.

Implémentation du service en Perl CGI

Pour perl, nous allons utiliser l’implémentation SOAP du module SOAP::Lite. Il faut donc au préalable l’installer si ce n’est pas déjà fait.

Comme il s’agit d’un CGI qui nous allons faire, nous allons créer le fichier /var/www/cgi-bin/SimpleService.cgi. L’implémentation est alors la suivante :

#!/usr/bin/perl -w

use SOAP::Transport::HTTP;

SOAP::Transport::HTTP::CGI
  -> dispatch_to('SimpleService')
  -> handle;

package SimpleService;

sub SayHello {
  return "Bonjour du monde SOAP de Perl/CGI (".shift.")";
}

sub GetNumber {
  return shift * 2;
}

et voilà c’est fini.

Implémentation du service en PHP/Apache

Nous utilisons nuSOAP. Si ce n’est pas déjà fait, il faut donc télécharger nuSOAP et placer le fichier nusoap.php dans le même répertoire que nos scripts PHP.

Après, il faut vérifier la configuration de php dans le fichier php.ini. En effet, pour que cela puisse fonctionner avec nuSOAP, il faut que la variable
always_populate_raw_post_data soit à On soit la ligne suivante :

always_populate_raw_post_data = On

(enfin au moins pour PHP 4.3.10 pour les autres version, je ne sais pas)

Nous placeront notre script PHP dans /var/www/html (avec le fichier nusoap.php) et allons l’appeler SimpleService.php. L’implémentation du service est alors très simple :

<?php
require_once('nusoap.php');

function SayHello($name = "") {
  return "Bonjour depuis le monde SOAP de PHP ($name)";
}

function GetNumber($number = "0") {
  return $number * 3;
}

$server = new soap_server;
$server->register('SayHello');
$server->register('GetNumber');

$server->service($HTTP_RAW_POST_DATA);
exit();
?>

et voilà, c’est fait. nuSOAP a aussi la possiblité de générer le fichier WSDL correspondant au service. Pour cela il est nécessaire de renseigner plus d’information lors de l’enregistrement via la méthode register. Voici le code du même service mais support la génération automatique du fichier WSDL :

<?php

require_once('nusoap.php');

function SayHello($name = "") {
  return "Bonjour depuis le monde SOAP de PHP ($name)";
}

function GetNumber($number = "0") {
  return $number * 3;
}

$server = new soap_server;

// Initialise le support WSDL
$server->configureWSDL('SimpleService', 'http://localhost/Simple');

$server->register('SayHello',           // method name
    array('name' => 'xsd:string'),      // input parameters
    array('return' => 'xsd:string'),    // output parameters
    'http://localhost/Simple',          // namespace (espace de nommage unique)
    'http://localhost/Simple#SayHello', // soapaction (fonction)
    'rpc',                              // style
    'encoded',                          // use
    'Dit bonjour a l\'appelant'         // documentation
);

$server->register('GetNumber',           // method name
    array('auction_id' => 'xsd:string'), // input parameters
    array('return' => 'xsd:int'),        // output parameters
    'http://localhost/Simple',           // namespace (espace de nommage unique)
    'http://localhost/Simple#GetNumber', // soapaction (fonction)
    'rpc',                               // style
    'encoded',                           // use
    'Converti une chaine en entier et multiple par 3' // documentation
);

$server->service($HTTP_RAW_POST_DATA);
exit();
?>

On peut alors accéder aux informations sur notre service SOAP à l’adresse http://localhost/SimpleService.php. On peut récupérer le fichier WSDL qui correspond à notre service à l’adresse http://localhost/SimpleService.php?wsdl. D’ailleurs c’est un très bon moyen de récupérer le fichier WSDL sans avoir à s’arracher la tête.

Ecrire un programme client en Perl

Nous utilisons toujours SOAP::Lite.

Voici un exemple de programme qui appel la méthode SayHello de notre service SOAP en Perl/CGI :

#!/usr/bin/perl -w

use SOAP::Lite;

print SOAP::Lite
  -> uri('http://localhost/Simple')
  -> proxy('http://localhost/cgi-bin/SimpleService.cgi')
  -> SayHello("test")
  -> result;

La même chose pour le même service mais en PHP :

#!/usr/bin/perl -w

use SOAP::Lite;

print SOAP::Lite
  -> uri('http://localhost/Simple')
  -> proxy('http://localhost/SimpleService.php')
  -> SayHello("test")
  -> result;

Ecrire un client PHP/Apache

Dans cet exemple, nous voulons appeler notre service SOAP depuis une page Web PHP :

<HTML>
<BODY>
<?php

include('nusoap.php');
$client = new soapclient('http://localhost/SimpleService.php');
//$client = new soapclient('http://localhost/cgi-bin/SimpleService.cgi');
$parametres = array('name'=>'PHP user');
?>
<?php
echo $client->call('SayHello', $parametres);
?>
</BODY>
</HTML>

Ecrire un client C# Mono

Avant toute chose, il vous faut une installation de Mono qui fonctionne.

Vous aurez aussi besoin des librairies suivantes :
 System.Runtime.Remoting.dll
 System.Web.Services.dll

elles sont disponible dans le package mono-web.

La première chose à faire est de générer une classe proxy qui permettra d’accéder au service SOAP distant comme si c’était une classe locale. Pour cela il faut le fichier WSDL que vous avez pu faire générer par PHP.

Pour transformer ce fichier WSDL en notre classe proxy, nous allons utiliser la commande wsdl :

wsdl SimpleService.wsdl

Cela nous génère le fichier SimpleService.cs. Il n’y a plus qu’à faire un petit programme pour tester, appelons le SimpleTest.cs :

using System;

namespace SimpleTest
{
  class SimpleClient
  {
    static void Main ()
    {
      SimpleService service = new SimpleService();
      // facultatif, permet de choisir une autre URL pour
      // le service si ce n'est pas celle par defaut
      // dans le fichier WSDL
      service.Url = "http://localhost:80/SimpleService.php";
      // service.Url = "http://localhost:80/cgi-bin/SimpleService.cgi";
      
      Console.WriteLine("Result: "+service.SayHello("coucou"));
    }
  }
}

il n’y a plus qu’à compiler le tout :

mcs -r:System.Runtime.Remoting.dll -r:System.Web.Services.dll SimpleTest.cs SimpleService.cs

et voilà un programme .Net qui peut appeler un service web PHP/Apache ou Perl/CGI.

Documents :

par Daniel Lacroix