Nous allons continuer notre apprentissage des Web Services dans cette troisième partie. Elle est consacrée à la mise en œuvre et au déploiement du Web Service, et l’appel par un client. Précédemment, nous avons vu la première notion importante, qui est la définition des Web Services, REST, WSDL, SOA, SOAP étant décrit dans des articles précédents, ainsi qu’une analyse de performance et de publication. La deuxième notion abordée pour ce thème est la définition des services de l’interface. Pour cela l’analyse du langage XML, WSDL permettant de décrire le mode de fonctionnement d’un Web Service, a été nécessaire.
Maintenant il est temps de créer notre serveur.
Tout d’abord, nous allons mettre en œuvre la méthode getResult()
, qui sera accessible en tant que fonction de service par la demande de messages entrants à partir du Web. Ensuite, nous allons créer un objet SoapServer
et communiquer avec la fonction de service à l’aide de la méthode SoapServer::addFunction()
. Comme vous le verrez, le constructeur SoapServer()
a un seul paramètre: le chemin du document WSDL qui décrit le service.
Mettre en œuvre et déployer le service
Après avoir vu la définition du service, l’étape suivante consiste à le mettre en œuvre en utilisant un langage de programmation. Dans mon cas je vais utiliser le PHP. Pourquoi ce langage ? Tout simplement parce que je développe tous les jours avec, et que les implémentations d’un serveur et d’un client sont très simples. Vous pouvez aussi bien utiliser JAVA, .NET, etc. Tout dépend de votre connaissance avec tel ou tel langage. L’écriture du code pour le service est très mécanique.
On va s’attaquer à notre serveur, préparez vos doigts ça va coder !
Nous allons d’abord créer un fichier nommé soap-server.php
. Jusque là tout le monde suit ? 🙂
Le SoapServer
peut fonctionner sans un document WSDL de la même manière que le SoapClient
le peut. Mais il n’y a pas d’avantages évidents à le mettre en place de cette façon. D’ailleurs nous verrons dans le prochain chapitre l’appel d’un client SOAP sans WSDL !
Voici le code source, les commentaires parlent d’eux-même :
<?php
/*
* Fonction getResult sert à addition ou soustraire 2 entiers et retourne le résultat
* @param $operation Type de l’opération (add/substract)
* @param $integer1 Entier 1
* @param $integer2 Entier 2
* @return $result Résultat de l’opération
*/
function getResult($operation, $integer1, $integer2) {
$result = 0;
if ($operation == "add") {
$result = $integer1 + $integer2;
}
if ($operation == "substract") {
$result = $integer1 -$integer2;
}
return $result;
}
// Désactivation du cache WSDL
ini_set("soap.wsdl_cache_enabled", "0");
// Catch l’erreur si l’instanciation la classe SoapServer
// échoue, on retourne l’erreur
try {
$server = new SoapServer(‘operation.wsdl’);
// On ajoute la méthode "getResult" que le serveur va gérer
$server->addFunction("getResult");
} catch (Exception $e) {
echo ‘erreur’.$e;
}
// Si l’appel provient d’une requête POST (Web Service)
if ($_SERVER[‘REQUEST_METHOD’] == ‘POST’) {
// On lance le serveur SOAP
$server->handle();
}
else {
echo ‘<strong>This SOAP server can handle following functions : </strong>’;
echo ‘<ul>’;
foreach($server -> getFunctions() as $func) {
echo ‘<li>’ , $func , ‘</li>’;
}
echo ‘</ul>’;
}
?>
[/sourcecode]
Dans un premier temps on définit la méthode getResult($operation, $integer1, $integer2)
et le traitement de celle-ci. Le retour sera le résultat de l’opération. Ensuite on instancie l’objet SoapServer
en mode WSDL. Pour cela, on lui passe en premier argument le WSDL operation.wsdl
(On suppose qu’il est dans le même répertoire que notre fichier soap-server.php
). Si vous voulez travailler en mode non WSDL il suffit de définir le paramètre à NULL
et déclarer l’URI.
Note : Ici, pour une raison de rapidité, j’ai seulement renseigné ce paramètre mais d’autres sont possibles. Je vous propose de lire la documentation ici.
Ensuite on ajoute la méthode getResult
au serveur qui va gérer les requêtes SOAP. Pour passer plusieurs fonctions au serveur il suffit d’utiliser un tableau de noms de fonctions.
$server->addFunction(array("getResult", "getInt"));
[/sourcecode]
Enfin on lance le serveur SOAP lorsque la requête est supposée être dans les données brutes POST de la requête HTTP. Dans le cas où le serveur ne reçoit pas une requête POST on affiche la liste des fonctions disponibles par le Web Service.
Vous enregistrez le fichier, et si tout se passe bien on se rendez-vous sur cette adresse : http://localhost/server-soap.php
Vous devriez voir :
Tout marche ?
Appel du Web Service par le client
Voici un client pour accéder à notre propre serveur SOAP.
Note : On suppose qu’il est dans le même répertoire que notre fichier soap-server.php
et operation.wsdl
$client = new SoapClient("operation.wsdl");
echo $client->getResult("add", 3, 4);
[/sourcecode]
C’est un peu plus facile, n’est-ce pas ?
Quels sont les problèmes avec WSDL ?
Le seul argument contre son utilisation est que le client doit charger systématiquement le document WSDL à partir du serveur, avant que la requête RPC soit faite, ce qui peut prendre beaucoup de temps dans un environnement Web. Afin d’accélérer les choses, on utilise une fonctionnalité de mise en cache WSDL. Celle-ci se fait en modifiant les variables de configuration se trouvant dans le php.ini
: soap.wsdl_cache_enabled
, soap.wsdl_cache_dir
et soap.wsdl_cache_ttl
. Par défaut, le cache WSDL est activée et le cache des fichiers WSDL est d’un jour.
Note : Il est également possible de modifier « à la volée » depuis votre script ces variables avec la méthode ini_set()
de PHP.
Voici la section SOAP pour votre fichier php.ini
avec les valeurs par défauts. Vous pouvez le coller dans votre propre fichier.
[soap]
soap.wsdl_cache_enabled = "1"
; enables or disables WSDL caching feature
soap.wsdl_cache_dir = "/tmp"
; sets the directory name where SOAP extension will put cache files
soap.wsdl_cache_ttl = "86400"
; (time to live) sets the number of second while cached file will be used
; instead of original one
[/sourcecode]
Quelles sont les zones à problème avec notre serveur et le client ?
Pour commencer, il ne traite pas les erreurs. Qu’advient-il lorsque le serveur ne reconnaît pas les paramètres demandés ? Le protocole SOAP spécifie un format particulier de messages pour signaler des erreursSoapFault
. Pour générer de tels messages, le serveur doit lever une exception en utilisant l’objet SoapFault
. Le premier paramètre du constructeur des erreurs SOAP est la chaîne du code d’erreur, le second est une chaîne de description de l’erreur.
function getResult($operation, $integer1, $integer2) {
$result = 0;
if (($operation != "add") && ($operation != "substract")) {
throw new SoapFault("Server", "Veuillez utiliser une methode d’operation valable (add/substract).");
}
if (!$integer1 || !$integer2) {
throw new SoapFault("Server", "Veuillez indiquer 2 entiers.");
}
if ($operation == "add") {
$result = $integer1 + $integer2;
}
if ($operation == "substract") {
$result = $integer1 -$integer2;
}
return $result;
}
[/sourcecode]
Il est nécessaire que le client doit être implémenté de telle manière à intercepter les exceptions renvoyées par SoapFault
.
try {
echo $client->getResult("divide", 3, 4);
} catch (SoapFault $e) {
echo $e;
}
[/sourcecode]
En second lieu, il est préférable d’encapsuler les fonctionnalités de Web Service dans une classe PHP. Dans ce cas, nous n’avons pas besoin d’utiliser des variables globales et ajoutez chaque méthode SOAP individuellement au serveur : on peut ajouter une classe entière, et toutes ses méthodes seraient accessibles via SOAP.
class Operation {
…
function getResult($operation, $integer1, $integer2) {
…
}
function getString($string) {
…
}
…
$server->setClass("Operation");
}
[/sourcecode]
Comme vous pouvez le voir, j’ai utilisé la méthode SoapServer::setClass()
pour ajouter à l’objet SoapServer
la classe Operation
.
Qu’est-ce qu’il y a dedans ?
Êtes-vous curieux de connaître le format du message SOAP, ou l’espoir de débugger votre Service Web ? Si oui, cette section est pour vous.
Le débogage d’un Web Service est toujours difficile. Le client interprète directement la réponse du Web Service et si la réponse n’est pas correctement formatée (qui ne respecte pas le format WSDL du service), il affiche une erreur du type :
Mais il ne renvoie pas l’erreur PHP du serveur SOAP.
Le constructeur SoapClient()
accepte un tableau associatif comme second paramètre. Différentes options peuvent être passées par ce tableau associatif. En voici seulement deux :
-
trace
: permet au client de stocker les demandes et les réponses SOAP (désactivées par défaut) -
exceptions
: permet au client de contrôler le mécanisme d’exception (activé par défaut)
Jetez un œil à l’exemple du client SOAP suivant. Il est dérivé de l’exemple précédent, et montre précisément les données transmises entre le client et le serveur. Afin de récupérer cette information, nous utilisons les méthodes __getLastRequest()
et __getLastResponse()
.
try {
echo $client->getResult($operation, $integer1, $integer2);
print "<pre>\n";
print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
print "</pre>";
} catch (SoapFault $e) {
echo $e;
}
[/sourcecode]
Voici la sortie du script. Il est un peu modifié, pour le rendre plus lisible et plus facile à comprendre.
Requête :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-calcul"
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/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getResult>
<operation xsi:type="xsd:string">add</operation>
<integer1 xsi:type="xsd:int">3</integer1>
<integer2 xsi:type="xsd:int">4</integer2>
</ns1:getResult>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
[/sourcecode]
Réponse :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-calcul"
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/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getResultResponse>
<result xsi:type="xsd:string">7</result>
</ns1:getResultResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
[/sourcecode]
D’autres implémentations de SOAP pour PHP
- PEAR::SOAP (http://pear.php.net)
- NuSOAP (http://dietrich.ganx4.com/nusoap)
- eZ SOAP (http://ez.no)
[samples id= »1343″]
Conclusion
Dans cet article, j’ai décrit seulement les fonctionnalités de base de l’extension SOAP pour PHP. En réalité, ce dernier est beaucoup plus complexe et permet de faire bien d’autre chose. Mais il n’est pas possible de vous montrer toutes ces caractéristiques dans un article aussi court.
L’extension SOAP est totalement documentée à ici.
Peut-être que ceux-ci seront des points de départ pour de futurs articles. D’ailleurs, une prochaine partie sera consacrée à l’utilisation de token, et sécurité des Web Services.
J’espère que cette partie vous a motivé à utiliser les Web Services, notamment en PHP. J’attends vos retours, sur les erreurs, remarques concernant le déploiement de vos propres Web Services.