Les Web services

Stéphane Genaud

Introduction

Le W3C définit un Web service: “ a software system designed to support interoperable machine-to-machine interaction over a network.

Le W3C distingue 2 types de web services:


Les services web REST-compliant (ou RESTful) sont présentés ici.

Web Services SOAP

Les web-services de type 'arbitrary' sont aujourd'hui implémentés dans leur écrasante majorité en SOAP et WSDL.

Pas de nouveauté conceptuelle par rapport à  d'autres propositions antérieures (e.g. DCE RPC ou CORBA IIOP), mais un engouement inégalé de la part de l'industrie (e.g. Microsoft, IBM, Sun Microsystems, BEA, HP, Oracle, SAP, ...).

Une part du succès de SOAP vient de son adossement au W3C, qui publie la spécification de SOAP sous la forme d'une recommandation.

Qu'est ce que SOAP ?

Définition du W3C:

SOAP

Dans les grandes lignes:

De par sa simplicité et les standards utilisés, SOAP doit permettre d'embarquer des services distants dans n'importe quelle application.

SOAP intérêts

De par sa simplicité et les standards utilisés, SOAP doit permettre d'embarquer des services distants dans n'importe quelle application.

architecture example

Exemple de message

<?xml version="1.0" encoding="UTF-8"?>
  <soap:Envelope
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
     <soap:Body>
        <po:purchaseOrder orderDate="2003-09-22" 
               xmlns:po="http://www.Monson-Haefel.com/jwsbook/PO">
          <po:accountName>Amazon.com</po:accountName>
          <po:accountNumber>923</po:accountNumber>
          <po:address>
             <po:name>AMAZON.COM</po:name>
             <po:street>1850 Mercer Drive</po:street>
             <po:city>Lexington</po:city>
             <po:state>KY</po:state>
             <po:zip>40511</po:zip>
          </po:address>
          <po:book>
             <po:title>J2EE Web Services</po:title>
             <po:quantity>300</po:quantity>
             <po:wholesale-price>24.99</po:wholesale-price>
          </po:book>
        </po:purchaseOrder>
     </soap:Body>
  </soap:Envelope>

Message SOAP - structure

message structure Un message SOAP est dans une Envelope, qui contient :

Remarques

Namespaces (encart)

Rappel: les namespaces (espaces de nommage) permettent d'éviter les conflits de noms. L'objectif est de permettre l'emploi de plusieurs langages dans un seul document.

<balise xmlns:mynsp="http://icps.u-strasbg.fr/course/sd/2005">
<mynsp:syllabus>
    ...
</mysnp:syllabus>
</balise>

Namespaces (encart - suite)

On peut utiliser des default namespaces pour ne pas avoir à  spécifier le préfixe à  chaque balise.
Ils sont définis avec xmlns sans préfixe.

<html xmlns="http://www.w3.org/1999/xhtml">
   <title>...</title>
</html>

Message SOAP - Header et Body

L'entête Header est optionnelle:

Le corps Body est obligatoire:

Messages RPC

SOAP 1.2 :
La définition du format des messages de style RPC se trouve dans la partie 2, section 4 de la recommandation.

Les éléments nécessaires à  un appel RPC sont :

  1. l'adresse du noeud SOAP final (destinataire),
  2. le nom du service (ou classe, ou méthode) à  invoquer,
  3. l'identification et le type des paramètres d'entrée et de retour.

Note: par analogie avec les modèles RPC existants, 1. est souvent dénommé proxy.

SOAP RPC requête/réponse

Soit la méthode foisdeux() de signature: int foisdeux(int).
La requête foisdeux(63) est encodé dans un message comme:
   <body>
     <ns1:foisdeux xmlns:ns1="urn:montest">
        <param1 xsi:type="xsd:int">63</param1>
     </ns1:foisdeux>
   </body>

Le message en réponse devrait être:

   <body>
     <ns1:foisdeuxResponse xmlns:ns1="urn:montest">
	  <return xsi:type="xsd:int">126</return>
     </ns1:foisdeux>
   </body>

Toujours un seul paramètre en retour, de type simple ou complexe. Utilise l'espace de nommage de XML Schema pour les types.

Spécification du service

Dans l'exemple précédent, l'utilisateur du service doit connaître, le nom des opérations disponibles, le type des entrées et sorties, à  quelle machine s'adresser, ....

Problème: comment faire pour changer un service web de machine ?
Réponse: Ne pas donner directement au client la localisation et les protocoles d'accès au service, mais une description du service qui contient ces informations. On peut alors changer la description: WSDL

Cette description peut être dans un fichier distribué, à une URL, ou localisée par un annuaire (UDDI).

WSDL

WSDL (Web Service Description Language) permet la description de web services dans un langage XML. C'est une note du W3C.

Permet de séparer la description des fonctionnalités abstraites offertes par un service, des détails concrets d'une description de service, tels que "comment" et "où" cette fonctionnalité est proposée.

Un document WSDL utilise les éléments suivants:

Element Définit
<portType> Les opérations proposées par le web service
<message> Les messages utilisés par le web service
<types> les types de données utilisées par le web service
<binding> Les protocoles de communication utilisés par le web service

Structure d'un document WSDL

<definitions>
<types>
... (utilise la notation des XML Schema) ....
</types>

<message>
...
</message>

<portType>
...
</portType>

<binding>
...
</binding>
</definitions>

Un doc. WSDL peut contenir d'autres éléments, e.g. les éléments extension et services permettant de gouper les définitions de plusieurs web services en 1 seul doc.

Hello World en WSDL

Exemple: description d'un service qui propose une opération HelloWorld.

Entête:

<definitions name="HelloService"
    targetNamespace="http://www.ecerami.com/wsdl/HelloService.wsdl"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://www.ecerami.com/wsdl/HelloService.wsdl"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

Définition des Namespaces pour éviter les conflits de noms. targetNamespace permet de référencer le document lui-même. Le namespace par défaut est http://schemas.xmlsoap.org/wsdl/ (i.e. sans préfixe, c'est le namespace utilisé).

Hello World en WSDL (message)

<message name="DisBonjourRequest">
   <part name="monom" type="xsd:string"/>
</message>
<message name="DisBonjourResponse">
   <part name="greeting" type="xsd:string"/>
</message>

Les deux messages définis correspondent aux entrées et sorties. Pour plusieurs arguments en entrée ou en sortie, on aurait plusieurs éléments <part>.

Hello World en WSDL (portType)

<portType name="bonjour_PortType">
   <operation name="DisBonjour">
      <input message="tns:DisBonjourRequest"/>
      <output message="tns:DisBonjourResponse"/>
   </operation>
</portType>

L'élément <portType> est le plus important: il définit le web service=l'opération (méthode) qu'on peut invoquer et les messages impliqués.

Hello World en WSDL (binding)

Indique par quel protocole les opérations vont être transportées.

<binding name="..." type="...">
      <soap:binding style="rpc|document" transport="...http..."/>
      <operation name="...">

		.... input et output de l'operation ...

      </operation >

</binding>

Hello World en WSDL (binding)

Indique par quel protocole les opérations vont être transportées. Le plus souvent, à  travers SOAP comme ici.

<binding name="bonjour_Binding" type="tns:bonjour_PortType">
      <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="DisBonjour">
         <soap:operation soapAction="DisBonjour"/>
         <input>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               namespace="urn:examples:helloservice"
               use="encoded"/>
         </input>
         <output>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               namespace="urn:examples:helloservice"
               use="encoded"/>
         </output>
      </operation>
</binding>

Outils d'analyze WSDL

Les WSDL sont avantageusement analysés par des logiciels:

Toolkits pour SOAP

Les messages XML sont la plupart du temps cachés au programmeur car il existe des outils existent dans de nombreux langages pour encoder/décoder les messages XML. (On parle de sérialisation/dé-sérialisation).

Quelques toolkits sont présentés ici:

Exemple client Perl

SOAP::Lite permet de construire une requête RPC de manière très concise de la forme:

  use SOAP::Lite;

  print SOAP::Lite -> uri($namespace)
                   -> proxy($service_URL)
                   -> $method_name($param)
                   -> result;
  • uri(): chaque proxy peut proposer plusieurs services (classes). L'URI est ici un nom unique de service.
  • proxy() est l'adresse du serveur à  contacter pour accéder à  la méthode. On peut utiliser les protocoles http:, mailto:, ou ftp:.
  • result est un scalaire. Dans le cas où le résultat est un vecteur il faut utiliser paramsout à  la place.

Exemple client Perl: BabelFish

Detailed Description
IMPORTANT NOTE: Our own access to the Altavista Babelfish data has been terminated, so this web service has been disabled. The service will remain down indefinitely until a suitable replacement translation engine can be found.

Appel au service de traduction BabelFish.
source : [ babelfish.pl ].

  #!perl -w

  use SOAP::Lite;

  print SOAP::Lite                                            
    -> uri('urn:xmethodsBabelFish')
    -> proxy('http://services.xmethods.net:80/perl/soaplite.cgi')
    -> BabelFish('en_fr','The weather is fine. Let us go.')
    -> result;

Exemple client Perl pour WSDL

SOAP::Lite permet d'invoquer indirectement une méthode décrite dans un fichier WSDL. Dans ce cas l'objet est construit par:

  use SOAP::Lite;

  print SOAP::Lite -> service($URL_to_wsdl_file)
                   -> $method_name($param);

Une requête est faite pour récupérer la description, la parse, construit le stub (i.e. l'objet équivalent évoquant le service).

Note 1: on peut invoquer plusieurs fois le service sans refaire la requête au WSDL :

  my $service= SOAP::Lite -> service('http://... .wsdl');
  print $service->method('a') + $service->method('a');

Note 2: le fichier peut être local :

  ->service('file:// ... .wsdl');

Exemple client Perl: BabelFish

Appel au service de traduction BabelFish à  travers une description WSDL.
source : [ babelfish2.pl ].

  #!perl -w

  use SOAP::Lite;

  print SOAP::Lite                                            
    -> service('http://www.xmethods.net/sd/2001/BabelFishService.wsdl')
    -> BabelFish('en_fr','The weather is fine. Let us go.');

Exemple client Java (Apache SOAP)

//
// XMethods sample client for the Stock Quote service
//
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;

public class stockquoteClient{

  public static float getQuote (URL url, String symbol) throws Exception {

    Call call = new Call ();
    // Service uses standard SOAP encoding
    String encodingStyleURI = Constants.NS_URI_SOAP_ENC;
    call.setEncodingStyleURI(encodingStyleURI);

    // Set service locator parameters
    call.setTargetObjectURI ("urn:xmethods-delayed-quotes");
    call.setMethodName ("getQuote");

    // Create input parameter vector
    Vector params = new Vector ();
    params.addElement (new Parameter("symbol", String.class, symbol, null));
    call.setParams (params);

    // Invoke the service ....
    Response resp = call.invoke (url,"");
(../..)

Exemple client Java (2/2)

    // ... and evaluate the response
    if (resp.generatedFault ()) {
	throw new Exception();
    } else {
      // Call was successful. Extract response parameter and return result
      Parameter result = resp.getReturnValue ();
      Float rate=(Float) result.getValue();
      return rate.floatValue();
    }
  }

// Driver to illustrate service invocation
  public static void main(String[] args) {
    try {
          URL url=new URL("http://services.xmethods.net:80/soap");
          String symbol= "TRW";
          float quote = getQuote(url,symbol);
          System.out.println(quote);
    }
    catch (Exception e) {e.printStackTrace();}
  }
}

PHP SOAP détaillé

PHP contient depuis PHP5 un support natif de SOAP (si compilé avec --enable-soap).

PHP dispose des classess suivantes pour manipuler le XML SOAP:

  • SoapClient : pour l'initialisation et l'appel des méthodes,
  • SoapHeader : pour construire un header personnalisé,
  • SoapParam : pour construire
  • SoapVar :
  • SoapFault : pour récupérer/afficher les erreurs.

Exemple client PHP

Etapes pour construire un client SOAP. Illustré ici avec PHP.

  1. Connaître l'adresse du WSDL
  2. Ecrire un client minimal pour analyser le WSDL
  3. Si des <xs:complexType> apparaissent, les étudier.
    Pour des paramètres
    • en entrée, créer les classes qui vont les représenter
    • en sortie, trouver comment parcourir la structure
  4. Ecrire l'appel de méthode avec les paramètres en entrée
  5. Ecrire l'extraction des résultats si il y a un retour

Etape 1 + 2

Client minimal pour analyzer le WSDL :
<?php
    $wsdl_url = "http://soap.amazon.com/schemas2/AmazonWebServices.wsdl";
    $client = new SoapClient( $wsdl_url );
	
    $ops = $client -> __getFunctions();
    print_r( $ops );
?>
Array
(
    [0] => ProductInfo KeywordSearchRequest(KeywordRequest $KeywordSearchRequest)
    [1] => ProductInfo PowerSearchRequest(PowerRequest $PowerSearchRequest)
    [2] => ProductInfo BrowseNodeSearchRequest(BrowseNodeRequest $BrowseNodeSearchRequest)
    [3] => ProductInfo AsinSearchRequest(AsinRequest $AsinSearchRequest)
    [4] => ProductLineArray BlendedSearchRequest(BlendedRequest $BlendedSearchRequest)
    [5] => ProductInfo UpcSearchRequest(UpcRequest $UpcSearchRequest)
    [6] => ProductInfo AuthorSearchRequest(AuthorRequest $AuthorSearchRequest)
    [7] => ProductInfo ArtistSearchRequest(ArtistRequest $ArtistSearchRequest)
    [8] => ProductInfo ActorSearchRequest(ActorRequest $ActorSearchRequest)
    [9] => ProductInfo ManufacturerSearchRequest(ManufacturerRequest $ManufacturerSearchRequest)
    [10] => ProductInfo DirectorSearchRequest(DirectorRequest $DirectorSearchRequest)
    [11] => ListingProductDetails ExchangeSearchRequest(ExchangeRequest $ExchangeSearchRequest)
    [12] => ProductInfo ListManiaSearchRequest(ListManiaRequest $ListManiaSearchRequest)
    [13] => ProductInfo WishlistSearchRequest(WishlistRequest $WishlistSearchRequest)
    [14] => SellerProfile SellerProfileSearchRequest(SellerProfileRequest $SellerProfileSearchRequest)
    [15] => SellerSearch SellerSearchRequest(SellerRequest $SellerSearchRequest)
    [16] => MarketplaceSearch MarketplaceSearchRequest(MarketplaceRequest $MarketplaceSearchRequest)
    [17] => ProductInfo SimilaritySearchRequest(SimilarityRequest $SimilaritySearchRequest)
    [18] => ShoppingCart GetShoppingCartRequest(GetShoppingCartRequest $GetShoppingCartRequest)
    [19] => ShoppingCart ClearShoppingCartRequest(ClearShoppingCartRequest $ClearShoppingCartRequest)
    [20] => ShoppingCart AddShoppingCartItemsRequest(AddShoppingCartItemsRequest $AddShoppingCartItemsRequest)
    [21] => ShoppingCart RemoveShoppingCartItemsRequest(RemoveShoppingCartItemsRequest $RemoveShoppingCartItemsRequest)
    [22] => ShoppingCart ModifyShoppingCartItemsRequest(ModifyShoppingCartItemsRequest $ModifyShoppingCartItemsRequest)
)

PHP SoapHeader

SoapHeader: permet de construire des entêtes personnalisées. Pour générer :
 <soap:Header>
    <Credentials xmlns="my.namespace.cred.steph">
      <login>string</login>
      <password>string</password>
    </Credentials>
  </soap:Header>
écrire en PHP :
<?php
   $login = "toto";
   $pasword="x1ert5!";
   $header = new SOAPHeader("my.namespace.cred.steph",
				"Credentials", 
				array(
					"login"=> $username,
					"password"=> $password,
				);
   /* on suppose qu'on a créé $client = new SoapClient(...) */
   $client -> __setSoapHeaders( $header );
?>

PHP SoapParam

SoapParam: permet de construire une partie des paramètres. Pour générer :
        <param1 xsi:type="xsd:int">63</param1>
        <param2 xsi:type="xsd:string">toto</param2>
écrire en PHP :
<?php
   $param1 = new SoapParam( (int)63, "param1");
   $param2 = new SoapParam( "toto" , "param2");
?>
Ces paramètres sont généralement utilisés dans un appel de méthode, qui va les englober dans une balise portant son nom. C'est SoapClient qui va la générer.

PHP SoapClient

SoapClient: après avoir préparé les entêtes et paramètres en entrée, on appelle une méthode associée au client. Cet appel va générer le message englobant les paramètres en entrée :
   <body>
     <ns1:foisdeux xmlns:ns1="urn:montest">
        <param1 xsi:type="xsd:int">63</param1>
     </ns1:foisdeux>
   </body>
écrire en PHP :
<?php
   $param1 = new SoapParam( (int)63, "param1");
   $client = new SoapClient( $url_wsdl );
   $client -> foisdeux( $param1 );
?>

Exercices

Attention: cet exercice nécessite de créer un compte chez Google (gratuité totale). Je n'ai aucun intérêt dans cette entreprise. Néanmoins, La contrepartie à  la création d'un compte est l'utilisation de services puissants qui justifient l'exercice.

Google fournit des services SOAP (moyennant la création d'une clé). Ceux ci sont décrits dans le fichier WSDL suivant : [ GoogleSearch.wsdl ].

  1. Créer un compte pour obtenir une clé.
  2. Ecrire un client SOAP::Lite qui donne le nombre de résultats obtenus (estimatedTotalResultsCount) par une recherche doGoogleSearch.
  3. Code source

Exercices

Le site XMethods a été historiquement le premier à recenser un ensemble de web-services publics.

D'autres, comme Service-Repository proposent une interface plus intuitive et surtout, un taux de disponibilité du service.

Parmi ceux-ci un service de localisation géographique capable de localiser une IP. La fiche de ce service est ici

Ecrire une requête SOAP pour trouver la latitude et longitude d'une IP dont vous connaissez l'emplacement géographique.

Pour les détails, voir l'exercice de mise en application: [ html ].

Conclusion

Mon sentiment personnel:

  • Beaucoup d'engouement pour SOAP initialement. Nombreuses implémentations, nombreux services à l'essai.
  • Beaucoup de projets abandonnés (question de la pérenité).
    Exemple: Service SOAP de recherche Google abandonné en 2009
  • L'approche REST semble aujourd'hui plus simple et robuste à mettre en place.