Un WebService (ou service Web selon les envies) représente une collection de fonctionnalités mises à disposition à travers les protocoles de l'Internet. Cela permet de sortir du carcan de la programmation monolithique réalisée par une seule personne - physique ou société - en autorisant l'interopérabilité avec d'autres langages et applications.
Il est fréquent de voir fleurir des applications permettant l'affichage de la météo à partir d'un site de référence autorisant l'interrogation de ses services gratuitement par exemple. Il existe en fait une quantité très large de webservices pour tous types d'usages.
Il n'est cependant pas évident de mettre tous ces éléments en place afin d'obtenir une chaîne de programmes qui fonctionnent car les règles à respecter sont assez strictes et parfois mal documentées (avec beaucoup de documents obscurs i.e. MSDN). C'est la raison pour laquelle cette page va essayer de rendre plus clairs la création et l'usage des services Web.
Les Web Services reposent sur un principe d'échanges de messages au format XML et utilisant le protocole HTTP comme protocole de transport de l'information. Les services Web peuvent être classés selon deux typologies :
Pour ma part je ne parlerai que de la version SOAP des WebServices, ce qui permettra de réaliser l'ensemble de ce que l'on souhaite obtenir avec un service Web. Le cadre suivi sera de réaliser le service Web en PHP (pour faire tourner le service sur n'importe quel site hébergé et pas seulement sur un VPS ou une machine louée à la Windows) et de proposer différents clients qui vont consommer ce service : un client PHP, un client python, un client en pur C, un autre en Java ainsi qu'un dernier réalisé avec le Framework .NET (oui tout de même). D'autres sont probablement à venir pour essayer d'avoir une vision la plus exhaustive possible...
Dans l'idée, le client qui veut consommer un Web Service va commencer par interroger la page Web afin de déterminer quelles sont les opérations qui sont mises à sa disposition. Ce sont les services exportés par le Web Service et avec lesquels il va être possible d'interagir. Une fois ces opérations récupérées, il ne reste plus qu'à lancer l'appel à ces méthodes afin de réceptionner le résultat que l'on espère... Les méthodes exposées par le web service sont regroupées au sein de fichiers de description au format WSDL dont nous verrons plus loin la composition.
Comme évoqué précédemment, l'objectif des deux technologies est le même : apporter une mise à jour, supprimer des éléments, faire des recherches... La méthode employée pour réaliser ces opérations diffère quelque peu au niveau des besoins et de la syntaxe à employer.
Prenons l'exemple d'une société qui souhaite pouvoir autoriser les actions suivantes :
REST | SOAP |
GET(URI) | listeProduits() |
PUT(URI, ressource) | MAJProduit(produits) |
POST(URI, ressource) | creerCommande(commande) |
DELETE(URI) | supprimerCommande(idCommande) |
La différence entre les deux apparaît clairement sur le type de requête à utiliser pour dialoguer avec le web service. Alors que REST n'emploie que les 4 commandes HTTP, SOAPpermet l'usage de commandes similaires à celles utilisées dans les langages de haut niveau.
L'objectif d'un web service est double : rapidité de fonctionnement et interopérabilité entre les divers langages de programmation. L'intérêt d'un web service est généralement de faire exécuter à un serveur distant une requête simple, qui ne monopolise pas de manière exclusive le serveur ayant la charge de l'exécution de cette requête, et qui ne charge pas le réseau de manière inconsidérée.
Les web services sont généralement organisés selon 4 axes :
Un fichier WSDL constitue l'épine dorsale du contrat que va offrir le service web à son client. Ce contrat est technique, et permet à l'application de connaître la manière d'invoquer le service. En aucun cas il n'y a d'information concernant une éventuelle tarification ou la mise en place d'une plaquette commerciale.
Un fichier WSDL est décrit selon une grammaire stricte reposant sur le format XML. Celle-ci sera décrite directement dans le code qui se situe plus loin dans le document.
J'ai décidé de mettre en place un service web à base de PHP pour une raison simple : la majorité des serveurs web reposent sur le paradigme Apache + Php + MySQL et représentent le plus faible investissement pour cette réalisation. Une autre raison pourrait être de ne pas m'imposer une architecture hyper propriétaire telle que celle de Microsoft et de son langage C# pour cette réalisation. Il aurait alors fallu mettre en place un hébergement sur une plateforme Windows (licence onéreuse) et mettre en place un hébergement Web reposant sur ASP + IIS. C'est moins immédiat et représente trop de contraintes pour un simple Web Service (même pour un Web service complexe... ;-) )
Il existe de nombreuses façons de mettre en place un serveur SOAP en PHP. Nous allons commencer par utiliser ce qui est normalement inclus d'origine avec tous les serveurs Web : la bibliothèque PHP-SOAP. Si vous ne savez pas si votre environnement PHP a été compilé avec le support de SOAP, créez simplement un fichier contenant <?php phpinfo(); ?>, enregistrez-le sur votre serveur sous le nom phpinfo.php (par exemple, mais tout autre nom fera l'affaire) et pointez votre navigateur internet vers ce fichier. Vous devriez obtenir ceci :
Avec notre service Web, nous n'allons faire que des choses simples : dire "Bonjour", additionner et soustraire deux nombres. Je sais que vous allez me dire qu'il aurait été plus judicieux de ne pas faire un exemple bateau mais proposer une interaction avec une base de données par exemple... Certes, mais on est ici pour comprendre le mécanisme du Web Service, pas pour développer une application globale non ? Je vous donne le code complet et les commentaires ensuite. Nous supposerons que le fichier du serveur s'appellera srvmath.php
<?php
/*
* L'utilisation d'une classe pour encapsuler l'ensemble des fonctions mises à disposition
* par le webservice est préférable à l'utilisation de fonctions dans le corps du fichier car Cela
* permet d'éviter l'usage trop important de $server->addFunction()
*/
class Math
{
public function Add($a, $b) {
return $a + $b;
}
public function Subtract($a, $b) {
return $a - $b;
}
public function Bonjour($qui) {
return "Bonjour $qui";
}
}
// Désactiver le cache WSDL lors de la phase de test
ini_set("soap.wsdl_cache_enabled", "0");
$server = new SoapServer(null,
array('uri' => 'http://<serveur>:<port>/srvmath.php'));
// Ajout de la classe Math dans les éléments proposés par le WebService
$server->setClass('Math');
$server->handle();
?>
Maintenant que nous avons le serveur en place, qui jouera le rôle de WebService, nous allons écrire un client qui va interroger ce serveur et afficher les informations renvoyées. Il s'appellera clientMath.php.'
<?php
ini_set("soap.wsdl_cache_enabled", "0");
$client = new SoapClient("http://<serveur>:<port>/srvmath.wsdl");
$ret = $client->Add(5, 7);
echo '5 + 7 = ' . $ret;
echo '<br />';
$ret = $client->Subtract(5, 7);
echo '5 - 7 = ' . $ret;
echo '<br />';
$ret = $client->Bonjour("Maître");
echo $ret;
?>
Pour permettre une bonne détection des différentes fonctions exportées par le serveur SOAP, il est recommandé (fortement même) de définir un fichier de description au format WSDL. C'est un peu compliqué à décrire, même si le format respecte le XML... Je vous livre le fichier dans son intégralité avec les commentaires pour les explications.
/*
* On commence par définir la version du format XML employé ainsi que l'éventuel encodage
* des caractères du document WSDL. Ce sera actuellement toujours le format 1.0 du langage
* XML qui sera utilisé.
*/
<?xml version='1.0' encoding='utf-8' ?>
/*
* L'élément racine du document se nomme definitions. Ce namespace donne le nom réel du
* service et peut prendre de nombreux attributs.
* Dans notre exemple, le webservice portera le nom de 'srvmath'.
*/
<definitions name='srvmath'
/*
* Création d'une section constituant la racine du document XML.
* Cette racine porte le nom 'srvmath' qui est en fait le nom qui va définir le service web.
* Des attributs supplémentaires sont présents, pas obligatoires mais constituant la liste
* des formats d'encodage de chaque rubrique.
* Je vous conseille de ne modifier que les 3 premiers avec le nom de votre service uniquement.
*/
targetNamespace='srvmath'
xmlns:tns='srvmath'
xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding'
xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
xmlns='http://schemas.xmlsoap.org/wsdl/'>
/*
* La section des messages constitue les informations transmises sur les files d'attente des
* services web. Ces files sont toujours au nombre de deux : une pour l'envoi (Request) et l'autre pour
* la réception des informations (Response).
* La fonction 'Add' du service web attend en entrée (Request) deux variables de
* type flottant, nommées 'a' et 'b'. La fonction retourne une valeur flottante nommée
* 'result' sur la file de message 'Response'.
* Même chose pour la fonction Subtract().
*/
<message name='AddRequest'>
<part name='a' type='xsd:float' />
<part name='b' type='xsd:float' />
</message>
<message name='AddResponse'>
<part name='result' type='xsd:float' />
</message>
<message name='SubtractRequest'>
<part name='a' type='xsd:float' />
<part name='b' type='xsd:float' />
</message>
<message name='SubtractResponse'>
<part name='result' type='xsd:float' />
</message>
/*
* Même chose pour la méthode 'Bonjour' qui déclare deux files de messages : une pour les
* paramètres en entrée, l'autre pour les valeurs de retour. Ici ce sont à chaque fois une
* valeur en paramètre d'entrée de type string et une valeur de retour de méthode qui est
* également de type string.
*/
<message name='BonjourRequest'>
<part name='qui' type='xsd:string' />
</message>
<message name='BonjourResponse'>
<part name='result' type='xsd:string' />
</message>
/*
* La section portType définit la connexion avec les différentes files de messages pour
* chaque sens de communication sur les opérations. Un portType associe des files de messages
* entrants et sortants.
* Nous avons ici un type nommé 'srvmathPortType' disposant de trois méthodes 'Add', 'Subtract' et 'Bonjour'.
* Ces méthodes définissent des files de messages input et output avec les noms logiques associés.
* Un port est donc une suite d'opérations que l'on pourrait qualifier de bibliothèque ou de module ou
* même de classe.
*/
<portType name='srvmathPortType'>
<operation name='Add'>
<input message='tns:AddRequest' />
<output message='tns:AddResponse' />
</operation>
<operation name='Subtract'>
<input message='tns:SubtractRequest' />
<output message='tns:SubtractResponse' />
</operation>
<operation name='Bonjour'>
<input message='tns:BonjourRequest' />
<output message='tns:BonjourResponse' />
</operation>
</portType>
/*
* La section binding décrit comment le webservice sera implémenté et la façon dont les messages
* seront transmis.
* Nous allons donc rattacher le port à SOAP, tout en sachant que les ports sont indépendants
* du protocole SOAP.
* Nous allons utiliser des requêtes SOAP (soap:binding) de type RPC (style='rpc') via du transport
* HTTP (transport='http://schemas.xmlsoap.org/soap/http') avec un encodage SOAP
* (encodingStyle='http://schemas.xmlsoap.org/soap/encoding/')
*/
<binding name='srvmathBinding' type='srvmathPortType'>
<soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http' />
<operation name='Add'>
<soap:operation soapAction='urn:xmethods-delayed-quotes#Add' />
<input>
<soap:body use='encoded' namespace='urn:xmethods-delayed-quotes' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
</input>
<output>
<soap:body use='encoded' namespace='urn:xmethods-delayed-quotes' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
</output>
</operation>
<operation name='Subtract'>
<soap:operation soapAction='urn:xmethods-delayed-quotes#Subtract' />
<input>
<soap:body use='encoded' namespace='urn:xmethods-delayed-quotes' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
</input>
<output>
<soap:body use='encoded' namespace='urn:xmethods-delayed-quotes' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
</output>
</operation>
<operation name='Bonjour'>
<soap:operation soapAction='urn:xmethods-delayed-quotes#Bonjour' />
<input>
<soap:body use='encoded' namespace='urn:xmethods-delayed-quotes' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
</input>
<output>
<soap:body use='encoded' namespace='urn:xmethods-delayed-quotes' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
</output>
</operation>
</binding>
/*
* La section service constitue le nom du service à invoquer ainsi que les ports utilisés.
* Un service est généralement composé d'une simple URL.
*/
<service name='srvmathService'>
<documentation>Des opérations et un message...</documentation>
<port name='srvmathPort' binding='srvmathBinding'>
<soap:address location='http://<serveur>:<port>/srvmath.php' />
</port>
</service>
</definitions>
Si vous avez suivi l'exemple ci-dessus, vous avez déjà pu avoir un aperçu de la façon dont peut se consommer un service Web via PHP. Nous allons voir différentes approches avec différents langages pour essayer de vraiment faire le tour de la question.
Avec Java
Avec Python
Avec Python
Avec le langage C