Nous écrivons une application SOAP client-serveur en PHP. Protocole SOAP. Concepts de base. Structure des messages SOAP

Protocole d'accès aux objets simples (SOAP) est un protocole basé sur XML qui définit les règles de transmission de messages sur Internet entre différents systèmes d'application. Il est principalement utilisé pour les appels de procédure à distance. SOAP a été conçu à l'origine pour fonctionner "au-dessus" de HTTP (pour faciliter l'intégration de SOAP dans les applications Web), mais d'autres protocoles de transport, tels que SMTP, peuvent désormais être utilisés.

Supposons que vous construisez un service d'accès aux applications sur Internet ; les consommateurs interagissent avec ce service en lui transmettant des informations. Vos serveurs traitent les données et renvoient les résultats aux consommateurs. Quelle est la meilleure façon de communiquer avec le système ?

Vous pouvez créer une application client/serveur personnalisée et demander aux consommateurs d'utiliser un programme client personnalisé pour accéder à votre service. Mais si vous envisagez sérieusement de vous lancer dans l'activité Internet, vous devrez créer un client qui s'exécute sur toutes les plates-formes client possibles : Windows, Macintosh, Unix, Linux, etc. En d'autres termes, vous devrez écrire de nombreux clients différents.

Que diriez-vous d'utiliser le Web ? Une telle solution, bien sûr, est tout à fait acceptable, mais est strictement liée à la mise en œuvre du navigateur, et encore une fois, vous devrez créer une infrastructure pour envoyer et recevoir des informations entrantes et sortantes, ainsi que des données de format et de package pour un tel échange. Pour les applications complexes, vous pouvez choisir Java ou ActiveX, mais certains utilisateurs refuseront alors vos services en raison d'exigences de bande passante manifestement excessives et d'une sécurité insuffisante.

Tout ce qu'il faut, c'est un protocole simple qui simplifie le conditionnement des données d'application et les transmet sur le Web à l'aide de XML adaptable au contenu de l'information. Ainsi, il garantit que l'expéditeur et le destinataire peuvent facilement interpréter le contenu de n'importe quel message. Dans le même temps, en utilisant le protocole Web HTTP comme moyen de transport, il sera possible d'éliminer la nécessité de réduire le niveau de protection des pare-feu.

Le protocole SOAP (Simple Object Access Protocol) bien décrit est un simple protocole "glue" par lequel les hôtes peuvent appeler à distance des objets d'application et renvoyer des résultats. SOAP fournit un ensemble minimum de conditions qui permettent à une application d'envoyer des messages : un client peut envoyer un message pour appeler un objet programme, et un serveur peut renvoyer les résultats de cet appel.

SOAP est assez simple : les messages sont des documents XML contenant des commandes SOAP. Alors qu'en théorie SOAP peut être lié à n'importe quel protocole de transport pour les applications, il est généralement utilisé en conjonction avec HTTP.

Kennard Scribner, co-auteur du livre Comprendre SOAP : la solution qui fait autorité(Macmillan USA, 2000) dit que SOAP fonctionne en convertissant les informations nécessaires pour invoquer une méthode (telles que les valeurs d'argument et les identifiants de transaction) au format XML.

Les données sont encapsulées dans HTTP ou un autre protocole de transport et sont transmises au destinataire, qui est généralement le serveur. Ce serveur extrait les données SOAP du paquet, effectue le traitement requis et renvoie les résultats sous forme de réponse SOAP.

Scribner a noté que SOAP agit comme un protocole d'appel de procédure à distance, un peu comme le protocole d'invocation de méthode à distance de Java ou le protocole général inter-ORB de CORBA.

Étant donné que HTTP et XML sont utilisés presque partout, Scribner affirme que SOAP est sans doute le protocole d'appel de procédure à distance le plus évolutif qui existe aujourd'hui. SOAP n'est pas conçu pour agir comme une architecture objet complète.

SOAP ne remplace pas Remote Method Invocation dans Java, le Distributed Component Object Model et CORBA ; il propose des règles qui peuvent être utilisées par n'importe lequel de ces modèles. SOAP n'est pas une solution complète. Il ne prend pas en charge l'activation ou la sécurité des objets. Selon Scribner, les concepteurs de SOAP "sont convaincus que les utilisateurs ajouteront eux-mêmes ce code" en le construisant au-dessus de SOAP plutôt qu'en l'intégrant au protocole lui-même.

La figure montre un exemple tiré de la spécification SOAP 1.1, dans lequel un hôte demande à un service de devis le prix d'une action particulière. La requête SOAP est intégrée dans un HTTP POST, et le corps de la requête spécifie le type de requête et le paramètre de caractère stock. La réponse fournit également un objet XML encapsulé dans une réponse HTTP avec une seule valeur de retour (34,5 dans ce cas).

Fonctionnalités SOAP

Avec SOAP, les développeurs peuvent créer des services Web aussi rapidement que des messages SOAP peuvent être écrits pour appeler des programmes applications existantes, puis ajoutez ces applications à des pages Web simples. Mais les développeurs ont également la possibilité d'utiliser les appels SOAP dans des applications dédiées et de créer des applications pouvant être transférées vers les pages Web d'autres personnes, évitant ainsi le processus de développement long et coûteux.

Selon Mark Stever, un autre auteur de Understanding SOAP, c'est exactement ce que vise Microsoft avec sa prochaine plate-forme .Net. "C'est là que SOAP brille dans toute sa splendeur. Cela donne aux développeurs un très bon moyen de créer des applications sans avoir à se soucier des incompatibilités potentielles », déclare-t-il.

Exemple SOAP

L'exemple suivant illustre une requête SOAP appelée GetLastTradePrice qui permet à un client d'envoyer une requête pour les dernières cotations d'une action spécifique.

POST/StockQuote HTTP/1.1
héberger: www.stockquoteserver.com
type de contenu : texte/xml ; jeu de caractères="utf-8"
longueur du contenu : nnnn
SOAPAction :"Some-URI"

Les cinq premières lignes (partie de l'en-tête HTTP) spécifient le type de message (POST), l'hôte, le type de charge utile et la longueur, et l'en-tête SOAPAction spécifie l'objectif de la requête SOAP. Le message SOAP lui-même est un document XML, où l'enveloppe SOAP vient en premier, puis élément XML A qui spécifie l'espace de noms et les attributs SOAP, le cas échéant. Une enveloppe SOAP peut inclure un en-tête (mais pas dans ce cas) suivi d'un corps SOAP. Dans notre exemple, le corps contient la requête GetLastTradePrice et le symbole boursier pour lequel les dernières cotations sont demandées. La réponse à cette requête peut ressembler à ce qui suit.

HTTP/1.1 200 OK
type de contenu : texte/xml ; jeu de caractères="utf-8"
longueur du contenu : nnnn

Encore une fois, les trois premières lignes font partie de l'en-tête HTTP ; le message SOAP lui-même consiste en une enveloppe qui contient la réponse à la demande d'origine, étiquetée GetLastTradePriceResponse, et inclut la valeur de retour, dans notre cas 34.5.

SOAP (protocole d'accès simple aux objets) est un protocole standardisé de transmission de messages entre un client et un serveur. Il est généralement utilisé en conjonction avec HTTP(S), mais peut également fonctionner avec d'autres protocoles de couche application (tels que SMTP et FTP).
Tester SOAP du point de vue des techniques de test n'est pas fondamentalement différent du travail avec d'autres API, mais cela nécessite une préparation préalable (en termes de théorie du protocole) et des outils spéciaux pour les tests. Dans cet article, je voudrais formuler une petite liste de contrôle des connaissances et compétences nécessaires qui seront tout aussi utiles à la fois pour un testeur SOAP (souvent n'ayant pas une idée de "à quoi s'accrocher" après avoir défini une tâche), et pour un manager obligé d'évaluer les connaissances des testeurs et d'élaborer des plans d'apprentissage.

Base théorique

Le fait que SOAP soit un protocole est d'une grande importance pour les tests : vous devez étudier le protocole lui-même, les normes et protocoles "primaires" sur lesquels il est basé, et (si nécessaire) les extensions existantes.

XML
XML est un langage de balisage similaire à HTML. Tout message envoyé/reçu via SOAP est un document XML dans lequel les données sont structurées de manière pratique et faciles à lire, par exemple :



Julia
Natasha
Rappel
N'oubliez pas d'écrire un article !


Vous pouvez en savoir plus sur XML sur w3schools ou codenet (en russe) . Assurez-vous de faire attention à la description des espaces de noms (une méthode pour résoudre les conflits lors de la description d'éléments en XML) - dans SOAP, leur utilisation est nécessaire.

XSD
Lorsque vous travaillez, il est toujours pratique d'avoir une description standardisée des documents XML possibles et de vérifier leur exactitude de remplissage. Il existe une définition de schéma XML (ou XSD en abrégé) pour cela. Les deux principales caractéristiques de XSD pour le testeur sont la description des types de données et l'imposition de restrictions sur les valeurs possibles. Par exemple, l'élément de l'exemple précédent peut être rendu facultatif et limité à 255 caractères en utilisant XSD :

...







...

Extensions SOAP
Dans votre travail, vous pouvez également rencontrer diverses "extensions" des normes SOAP telles que WS-* . L'un des plus courants est WS-Security, qui vous permet de travailler avec le chiffrement et signatures électroniques. Souvent, une WS-Policy est utilisée avec elle, avec laquelle vous pouvez gérer les droits d'utilisation de votre service.

Un exemple d'utilisation de WS-Security :


Alice
6S3P2EWNP3lQf+9VC3emNoT57oQ=
YF6j8V/CAqi+1nRsGLRbuZhi
2008-04-28T10:02:11Z

Toutes ces extensions sont des constructions assez complexes qui ne sont pas utilisées dans tous les services SOAP ; les étudier en détail au stade initial de la maîtrise des tests SOAP est peu susceptible d'être pertinent.

Outils

Comme vous l'avez déjà compris, SOAP est une affaire sérieuse, pour travailler avec, vous devez connaître la théorie et de nombreuses normes. En pratique, une telle complexité entraînerait des coûts de main-d'œuvre très tangibles (par exemple, il faudrait regarder le schéma dans un cahier à chaque fois et envoyer des requêtes curl). Par conséquent, des outils ont été créés pour faciliter le travail avec SOAP.

Éditeurs XML/XSD
Un bon testeur commence à tester au stade de la rédaction de la documentation, il est donc pratique d'utiliser des éditeurs spéciaux pour vérifier les schémas. Les deux plus célèbres sont Oxygen (multiplateforme) et Altova (Windows uniquement) ; les deux sont payés. Ce sont des programmes très puissants que les analystes utilisent activement pour décrire les services.

Dans ma pratique, trois fonctionnalités des éditeurs se sont avérées utiles : la visualisation XSD, la génération XML basée sur XSD et la validation XML sur XSD.

1. Visualisation XSD nécessaire pour une représentation visuelle du schéma, vous permettant d'isoler rapidement les éléments et attributs requis, ainsi que les restrictions existantes. Par exemple, pour une requête CheckTextRequest, l'élément de texte est obligatoire et les trois attributs sont facultatifs (avec l'attribut options défini sur la valeur par défaut de zéro).

La visualisation est nécessaire lorsqu'il existe de nombreux types et restrictions dans le schéma. Si vous en avez seulement besoin et que vous ne voulez pas payer pour des éditeurs dédiés, des alternatives gratuites (telles que JDeveloper) peuvent être envisagées.

2. Génération XML basée sur XSD utile lorsque vous voulez voir un exemple valide de message. Je l'utilise pour expérimenter rapidement l'achèvement possible du message et vérifier les nuances du fonctionnement des restrictions.

3. Après avoir utilisé la fonction du point 2, il est utile de Validation XML contre XSD- c'est-à-dire vérifier l'exactitude du message. Ensemble, les fonctionnalités 2 et 3 vous permettent de détecter les défauts délicats dans XSD même lorsque le service lui-même est en cours de développement.

Outil de test - SoapUI

Les tests SOAP impliquent presque toujours l'utilisation de SoapUI . Vous pouvez lire sur l'utilisation de cet outil dans diverses sources (,), mais il sera plus efficace de lire la documentation officielle. Je distingue 8 niveaux conditionnels de maîtrise de SoapUI :

Niveau 1 - Je peux envoyer des demandes
Apprenez à créer un projet basé sur WSDL. SoapUI peut générer toutes les requêtes nécessaires pour vous ; il vous suffit de vérifier l'exactitude de leur remplissage et de cliquer sur le bouton "Envoyer". Après avoir maîtrisé l'art de faire des requêtes valides, vous devez maîtriser l'art de faire des requêtes non valides qui causent des erreurs.

Niveau 2 - Je peux faire des suites de tests et des cas de test
Commencez à faire des mini-autotests. Les suites de tests et les scénarios de test vous permettent de créer des scripts de test d'API, de préparer des données pour les demandes et de vérifier automatiquement la réponse reçue par rapport à celle attendue. Au début, ils peuvent être utilisés simplement comme des collections de requêtes. Par exemple, si vous avez un défaut et que vous souhaitez le vérifier rapidement après un correctif, vous pouvez allouer une suite de tests distincte spécifiquement pour les demandes de défaut.

Niveau 3 – peut écrire des Assertions
Après avoir maîtrisé les cas de test, il vous sera utile d'apprendre à les rendre automatiquement vérifiables. Après cela, vous n'aurez plus besoin de chercher des informations sur la réponse avec vos "yeux": si vous avez vérification automatique les cas seront marqués en vert (si le test est réussi) ou en rouge (s'il n'est pas réussi). SoapUI fournit un grand ensemble contrôles éventuels(affirmations), mais les plus pratiques et les plus simples sont Contient et Ne contient pas. Avec leur aide, vous pouvez vérifier la présence d'un texte particulier dans la réponse reçue. Ces vérifications prennent également en charge les recherches d'expressions régulières.

Niveau 4 - utilisation de XPath et/ou XQuery dans les Assertions
Pour ceux qui sont un peu familiers avec l'interface utilisateur utilisant Selenium, le langage XPath est une chose familière. En gros, XPath vous permet de rechercher des éléments dans un document XML. XQuery est une technologie similaire qui peut utiliser XPath en interne ; ce langage est beaucoup plus puissant, il ressemble à SQL. Ces deux langages peuvent être utilisés dans les Assertions. Les contrôles avec leur aide sont plus ciblés et stables, vos cas seront donc plus crédibles.

Niveau 5 - peut écrire des tests complexes en utilisant des étapes spéciales

Les cas de test peuvent contenir non seulement une requête, mais également plusieurs (par exemple, lorsque vous souhaitez émuler un scénario utilisateur standard "créer une entité" → "exporter une entité"). Il peut y avoir d'autres étapes spéciales entre les demandes, telles que :

  • Propriétés et transfert de propriété (aide à réutiliser les données et à les transférer entre les demandes) ;
  • Requête JDBC (utilisée pour obtenir des données de la base de données) ;
  • Goto conditionnel (permet de créer des branches ou des boucles dans un cas de test) ;
  • Exécutez TestCase (permet de placer certaines requêtes typiques dans des cas de test distincts et de les appeler si nécessaire).

Niveau 6 - utilisation de scripts Groovy

SoapUI vous permet d'écrire des scripts Groovy à divers endroits. Le cas le plus simple consiste à générer des données dans la requête elle-même à l'aide d'insertions $(=). J'utilise ces plugins tout le temps:

  • $(=nouvelle Date().format("aaaa-MM-jj'T'HH:mm:ss"))– pour insérer la date et l'heure actuelles dans le format requis ;
  • $(=java.util.UUID.randomUUID())– pour insérer un GUID aléatoire bien formé.

Des scripts complets peuvent être utilisés comme étapes dans des cas et des tests. À un moment donné, vous constaterez que plusieurs étapes spéciales du cinquième niveau peuvent être remplacées par un seul script à la fois.

Niveau 7 - en utilisant MockServices
SoapUI basé sur WSDL peut générer des objets Mock. Un objet fictif est la simulation la plus simple d'un service. Avec l'aide de "simulacres", vous pouvez commencer à écrire et à déboguer des cas de test avant même que le service ne soit réellement disponible pour les tests. Ils peuvent également être utilisés comme "stubs" pour des services temporairement indisponibles.

Niveau 8 - dieu SoapUI
Connaissez-vous la différence entre payé et versions gratuites SoapUI et utilisez l'API SoapUI dans le code. Vous utilisez des plugins et exécutez des cas via la ligne de commande et/ou CI. Vos cas de test sont simples et faciles à entretenir. En général, vous "mangez le chien" sur cet instrument. J'aimerais parler à quelqu'un qui maîtrise SoapUI à ce niveau. Si vous êtes, s'il vous plaît laissez un commentaire!

Tester avec des langages de programmation

Je vais donner un exemple de ce à quoi ressemble une requête à l'API YandexSpeller, faite à l'aide de groovy-wslite :

importer wslite.soap.*
def client = new SOAPClient("http://speller.yandex.net/services/spellservice?WSDL")
def réponse = client.send(SOAPAction : "http://speller.yandex.net/services/spellservice/checkText") (
corps (
CheckTextRequest("lang": "ru", "xmlns":"http://speller.yandex.net/services/spellservice") (
texte("erreur")
}
}
}
assert "error" == response.CheckTextResponse.SpellResult.error.s.text()
affirmer "1" == [courriel protégé]()

Pour autant que je sache, il n'existe pas encore de framework de haut niveau (comme Rest-assured) pour les tests SOAP, mais un outil intéressant est récemment apparu - karate . Avec lui, vous pouvez décrire des cas pour tester SOAP et REST sous la forme de scénarios comme Cucumber / Gherkin. Pour de nombreux testeurs, se tourner vers le karaté sera la solution idéale, car de tels scénarios, en termes de complexité d'écriture et de maintenance des cas, se situeront quelque part entre l'utilisation de SoapUI et l'écriture de votre propre framework de test SOAP.

Conclusion

Il est peu probable que vous vouliez un jour tester SOAP comme ça, par vous-même (comme vous pourriez le faire avec REST). Il s'agit d'un protocole lourd qui est utilisé dans de graves solutions d'entreprise. Mais sa lourdeur est aussi un cadeau pour le testeur : toutes les technologies utilisées sont standardisées, il existe des outils de travail de grande qualité. Tout ce qui est demandé au testeur est le désir de les étudier et de les utiliser.

Mettons ensemble la même liste de contrôle des compétences nécessaires pour un testeur. Donc, si vous commencez tout juste à tester les services SOAP, vous devez connaître et être capable d'utiliser :

  • wsdl.
  • SAVON.
  • Éditeurs XML / XSD (au niveau de la visualisation XSD).
  • SoapUI au niveau 1.

Comme vous pouvez le voir, l'accent est mis sur les normes d'apprentissage, dans SoapUI, il suffit de pouvoir exécuter des requêtes. En plongeant dans les tests SOAP, vous serez confronté à des tâches qui nécessiteront des compétences et des connaissances plus sérieuses, mais vous ne devriez pas essayer de tout apprendre en même temps. Beaucoup plus important est la cohérence dans l'augmentation du niveau de complexité des tâches effectuées. Suite à cette recommandation, vous réaliserez un jour que vous êtes devenu un bon spécialiste dans ce domaine !

Brett McLaughlin Traduction par Ilya Chekmenev

SOAP est le protocole d'accès simple aux objets. Si vous n'avez jamais entendu parler de lui auparavant, alors vous devez vivre dans un désert, loin de la civilisation. C'est devenu la dernière mode de la programmation web, et une partie intégrante des services web qui sont utilisés avec tant de fanatisme dans développement web la dernière génération. Si vous avez entendu parler de .NET de Microsoft ou de la "révolution peer-to-peer", alors vous avez entendu parler des technologies basées sur SOAP (même si vous ne savez pas ce que c'est). Il n'y en a pas un mais deux implémentations de SOAP, d'Apache et de Microsoft, qui ont des milliers de pages sur leur site de support technique MSDN (http://msdn.microsoft.com/).

Dans cet article, je vais vous expliquer ce qu'est SOAP et pourquoi il joue un rôle si important dans l'évolution du paradigme. programmation web. Cela vous aidera à ignorer les principes fondamentaux et à vous mettre directement au travail avec la boîte à outils SOAP. Je donne ensuite un aperçu rapide des projets SOAP existants et plonge dans l'implémentation d'Apache. Cet article n'a pas la prétention de recréer l'image complète de SOAP, mon livre "Java & XML 2nd Edition" comble pas mal de lacunes. Vous trouverez dans le livre des réponses à de nombreuses questions qui se sont posées après la lecture de cet article.

Introduction

Vous devez d'abord comprendre ce qu'est SOAP. Vous pouvez lire l'avis complet (et assez long) du W3C sur http://www.w3.org/TR/SOAP. Ensuite, après avoir compris et jeté toutes les enveloppes, vous comprendrez que SOAP n'est qu'un protocole. C'est un protocole simple (pas besoin d'en écrire un nouveau pour l'utiliser) basé sur l'idée qu'à un moment donné dans une architecture distribuée il y a un besoin d'échanger des informations. De plus, pour les systèmes où il existe une possibilité de surcharges et des difficultés dans les processus de traitement, ce protocole est très avantageux en ce qu'il est léger et nécessite un minimum de ressources. Enfin, il permet d'effectuer toutes les opérations sur HTTP, ce qui permet de contourner des éléments délicats comme les pare-feu et de se protéger de l'écoute sur des sockets avec un nombre impensable de ports. L'essentiel est que vous vous en rendiez compte, et tout le reste n'est que détails.

Bien sûr, vous aimeriez connaître ces détails, et je ne les ignorerai pas. Il existe trois composants de base dans la spécification SOAP : l'enveloppe SOAP, un ensemble de règles de chiffrement et les moyens d'interaction entre une requête et une réponse. Considérons un message SOAP comme une lettre normale. Vous souvenez-vous encore de ces choses anciennes dans des enveloppes avec un timbre-poste et une adresse écrite au recto ? Cette analogie aide à visualiser plus clairement le concept de SOAP comme une "enveloppe". La figure 12-1 décrit les processus SOAP sous la forme de cette analogie.

Illustration 12-1. Processus de message SOAP

Gardez cette image à l'esprit et examinons les trois composants de la spécification SOAP. Je parlerai brièvement de chacun d'eux, en donnant des exemples qui représentent le mieux ce concept. Ces trois composants clés rendent SOAP si important et significatif. La gestion des erreurs, la prise en charge de divers chiffrements, la sérialisation des paramètres et le fait que SOAP fonctionne sur HTTP dans la plupart des cas le rendent plus attrayant que d'autres solutions de protocole distribué. SOAP offre un degré élevé d'interopérabilité avec d'autres applications, que j'ai décrites plus en détail dans mon livre. Pour l'instant, je veux me concentrer sur les éléments de base de SOAP.

Enveloppe

L'enveloppe SOAP est similaire à une enveloppe de courrier ordinaire. Il contient des informations sur le message qui seront chiffrées dans la section SOAP principale, y compris des informations sur le destinataire et l'expéditeur, ainsi que des informations sur le message lui-même. Par exemple, un en-tête d'enveloppe SOAP peut indiquer comment le message doit être traité. Avant qu'une application ne commence à traiter un message, elle analyse les informations sur le message, y compris si elle peut même traiter le message. Contrairement à la situation avec les appels XML-RPC standard (rappelez-vous ? Messages XML-RPC, cryptage, etc., tous combinés en un seul fragment XML), avec SOAP, le traitement réel a lieu afin de découvrir quelque chose sur le message. Un message SOAP typique peut également inclure un style de cryptage pour aider le destinataire à traiter le message. L'exemple 12-1 montre une enveloppe SOAP qui se termine par une spécification d'encodage.

Exemple 12-1 : Enveloppe SOAP

Boîte à savon http://www-106.ibm.com/developerworks/library/x-soapbx1.html

Comme vous pouvez le voir, le cryptage est défini à l'intérieur de l'enveloppe, ce qui permet à l'application de déterminer (à l'aide de la valeur d'attribut encodingStyle) s'il peut lire le message entrant situé dans l'élément Corps. Assurez-vous que l'espace de noms de l'enveloppe SOAP est correct, sinon les serveurs SOAP qui reçoivent votre message lanceront une erreur d'incompatibilité de version et vous ne pourrez pas interagir avec eux.

Chiffrement

Le deuxième élément important de SOAP est la possibilité de chiffrer des types de données personnalisés. Dans RPC (et XML-RPC), le chiffrement ne peut être effectué que sur des types de données prédéfinis pris en charge dans la boîte à outils XML-RPC que vous avez téléchargée. Le chiffrement d'autres types de données nécessite que vous modifiiez vous-même le serveur et le client RPC. Avec SOAP, un schéma XML peut être utilisé assez facilement pour spécifier de nouveaux types de données (en utilisant le typecomplexe, discuté dans le chapitre 2 de mon livre), et ces nouveaux types peuvent être représentés en XML dans le cadre de la section SOAP principale. Grâce à l'intégration du schéma XML, vous pouvez chiffrer tout type de données dans un message SOAP en le décrivant logiquement dans un schéma XML.

Appel

La meilleure façon de comprendre le fonctionnement d'un appel SOAP est de le comparer à quelque chose que vous connaissez, comme XML-RPC. Si vous vous en souvenez, l'appel XML-RPC ressemble à l'extrait de code présenté dans l'exemple 12-2.

Exemple 12-2. Appel à XML-RPC

// Spécifier le gestionnaire XML (parser) pour utiliser XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser"); // Spécifier le serveur pour se connecter à XmlRpcClient client = new XmlRpcClient("http://rpc.middleearth.com"); // Crée des paramètres Vector params = new Vector(); params.addElement(numérodevol); params.addElement(numSeats); params.addElement(creditCardType); params.addElement(creditCardNum); // Requête Boolean buyerTickets = (Boolean)client.execute("ticketCounter.buyTickets", params); // Traitement de la réponse

J'ai créé un programme simple pour commander des billets d'avion. Examinez maintenant l'exemple 12-3, qui illustre un appel SOAP.

Exemple 12-3. Appel à SOAP

// Crée des paramètres Vector params = new Vector(); params.addElement(new Parameter("flightNumber", Integer.class, flightNumber, null)); params.addElement(new Parameter("numSeats", Integer.class, numSeats, null)); params.addElement(nouveau Paramètre("creditCardType", String.class, creditCardType, null)); params.addElement(new Parameter("creditCardNumber", Long.class, creditCardNum, null)); // Créer un objet Call Appeler appeler= nouvel appel(); call.setTargetObjectURI("urn:xmltoday-airline-tickets"); call.setMethodName("buyTickets"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); appeler setParams(params); // Réponse d'appel res = call.invoke(new URL("http://rpc.middleearth.com"), ""); // Traitement de la réponse

Comme vous pouvez le voir, l'appel lui-même, représenté par l'objet appel, résidant dans la mémoire. Il vous permet de définir la cible d'appel, la méthode d'appel, le style de chiffrement, les options et de nombreuses autres options non présentées dans cet exemple. Il s'agit d'un mécanisme plus souple que la méthode XML-RPC, qui vous permet de définir explicitement un ensemble d'options diverses implicitement définies dans XML-RPC. Plus loin dans cet article, vous en apprendrez plus sur le processus d'appel, y compris la façon dont SOAP gère les demandes non valides, la hiérarchie des erreurs et bien sûr les résultats de retour de l'appel.

Après un tel courte introduction vous en savez déjà assez pour vous intéresser à cette drôle de chose. Permettez-moi maintenant de vous présenter l'implémentation SOAP que je vais utiliser. Je vais expliquer les raisons pour lesquelles je l'ai choisi et regarder quelques exemples de code.

Paramètre

Maintenant que vous avez couvert les bases du concept, il est temps de passer à la partie amusante : la programmation. Pour ce faire, vous avez besoin d'un projet ou d'un produit pratique, plus facile à trouver qu'il n'y paraît à première vue. Si vous avez besoin d'un projet Java qui fournit des fonctionnalités SOAP, ne cherchez pas plus loin. Il existe deux groupes de produits : commerciaux et gratuits. Comme dans mon livre, j'éviterai de mentionner les produits commerciaux. Ce n'est pas du tout parce qu'ils sont mauvais (au contraire, certains d'entre eux sont excellents), mais parce que j'aimerais que tout lecteur puisse essayer n'importe lequel des exemples donnés. Cela est dû à la disponibilité que de nombreux produits commerciaux n'ont pas. Vous devez payer pour les utiliser, ou les utiliser temporairement pendant une période limitée après le téléchargement.

Ainsi, nous avons abordé en douceur les projets open source. De ce domaine, je ne peux citer qu'un seul produit : Apache SOAP. Il se trouve sur http://xml.apache.org/soap et fournit une boîte à outils SOAP pour Java. Au moment d'écrire ces lignes, la version 2.2 est sortie et vous pouvez la télécharger depuis le site Web d'Apache. C'est cette version que j'utiliserai dans les exemples de cet article.

Autres alternatives

Avant de passer à l'installation et à la configuration d'Apache SOAP, je vais répondre à quelques questions qui vous ont peut-être traversé l'esprit. Je pense avoir expliqué assez clairement pourquoi je n'utilise pas de produits commerciaux. Cependant, vous pensez peut-être à d'autres projets open source ou connexes que vous aimeriez utiliser, et vous êtes surpris que je ne les ai pas commentés.

Qu'en est-il d'IBM SOAP4J ?

La première sur la liste des alternatives est une implémentation d'IBM : SOAP4J. Le travail d'IBM a constitué la base du projet Apache SOAP, tout comme XML4J d'IBM a évolué pour devenir ce qui est maintenant connu sous le nom de projet d'analyseur XML Apache Xerces. Il est prévu que l'implémentation d'IBM soit repensée, fusionnant avec Apache SOAP. À peu près la même chose s'est produite avec XML4J d'IBM : désormais, il ne fournit que des packages dans Xerces. Cela ne fait que souligner les tendances : les grands fabricants prennent souvent en charge et utilisent des projets OpenSource, dans ce cas, les deux projets (Apache et IBM) utilisent la même base de code.

Microsoft est-il hors jeu ?

Bien sûr que non. Microsoft et son implémentation de SOAP, ainsi que l'ensemble de la branche .NET (plus à ce sujet dans mon livre), sont importants. En fait, je voulais passer la plupart de mon temps à étudier en détail l'implémentation SOAP de Microsoft, mais elle ne prend en charge que les objets COM et ne prend pas en charge Java. Pour ces raisons, une telle description ne pourrait pas être incluse dans un article sur Java et XML. Cependant, Microsoft (malgré tous les griefs que nous, en tant que développeurs, avons contre cette société) a fait un travail important dans le domaine des services Web, et vous ferez une erreur si vous le rejetez sans hésitation, guidé uniquement par des émotions brutes. Si vous avez besoin de travailler avec des composants COM ou Visual Basic, je vous recommande fortement d'essayer d'utiliser la boîte à outils Microsoft SOAP disponible à l'adresse http://msdn.microsoft.com/library/default.asp?url=/nhp/Default. asp ?contentid=28000523 ainsi que de nombreuses autres ressources SOAP.

Qu'est-ce qu'Axis ?

Ceux d'entre vous qui suivent Apache doivent avoir entendu parler d'Apache Axis. Axis est la boîte à outils SOAP de nouvelle génération, également en cours de développement sous les auspices d'Apache XML. SOAP (une spécification, pas une implémentation spécifique), qui a évolué rapidement et radicalement ces derniers temps, est très difficile à suivre. Essayer de créer une version de SOAP entièrement conforme aux exigences actuelles qui changent au cours du développement est également assez difficile. Par conséquent, la version actuelle d'Apache SOAP offre une solution limitée par sa conception. Décidant qu'il ne valait pas la peine d'essayer de repenser complètement l'outil existant, les développeurs d'Apache se sont mis à créer un projet basé sur le nouveau code. C'est ainsi qu'Axis est né. Le nom de SOAP a également changé, d'abord de SOAP à XP puis à XMLP. Ensuite, le nom de la spécification a été supprimé du nom du nouveau SOAP et le nom "Axis" est né. Mais maintenant, il semble que le W3C revienne au nom de la spécification SOAP (version 1.2 ou 2.0), donc les choses peuvent encore changer et il y aura encore plus de confusion !

Considérez IBM SOAP4J comme l'architecture1 de la boîte à outils SOAP. Qu'en est-il d'Apache SOAP (abordé dans cet article) en tant qu'architecture ?2. Et Axis représente l'architecture?3, l'architecture de nouvelle génération. Ce projet utilise SAX tandis qu'Apache SOAP est basé sur DOM. De plus, Axis, contrairement à Apache SOAP, offre une approche plus conviviale de l'interaction avec l'utilisateur. Après avoir énuméré ces avantages, vous vous demanderez probablement pourquoi je n'ai pas choisi Axis comme sujet d'étude. Ce serait juste un peu prématuré. Actuellement, seule la version 0.51 d'Axis est en préparation pour la sortie. Ce n'est pas encore bêta, et même pas alpha. J'aimerais couvrir les nouvelles fonctionnalités d'Axis, mais vous ne pourrez en aucun cas convaincre votre direction d'utiliser un logiciel open source pré-alpha pour les besoins de votre système le plus important. Alors j'ai décidé de me concentrer sur quelque chose que tu es réel vous pouvez utiliser déjà Aujourd'hui- SAVON Apache. Je pense qu'au moment où la version finale d'Apache Axis sera publiée, je mettrai à jour ce matériel dans la prochaine édition de mon livre. D'ici là, concentrons-nous sur la solution déjà disponible.

Installation

Il existe deux formes de configuration SOAP. La première consiste à lancer un client SOAP, en utilisant l'API SOAP pour communiquer avec un serveur pouvant recevoir des messages SOAP. La deuxième méthode consiste à démarrer un serveur SOAP qui peut recevoir des messages d'un client SOAP. Dans cette section, j'ai décrit les deux procédures.

Client

Pour utiliser le client SOAP, vous devez d'abord télécharger Apache SOAP, disponible à l'adresse http://xml.apache.org/dist/soap . J'ai téléchargé la version 2.2 au format binaire (du sous-répertoire version-2.2). Ensuite, vous devez décompresser le contenu de l'archive dans un répertoire de votre ordinateur. Dans mon cas c'était le répertoire javaxml2 (c:\javaxml2 sur mon ordinateur Windows /javaxml2 sur mon ordinateur Mac OS X). En conséquence, les fichiers ont été décompressés dans /javaxml2/soap-2_2. Vous devrez également télécharger le package JavaMail disponible sur le serveur Sun http://java.sun.com/products/javamail/ . Il sera nécessaire de supporter le protocole de transfert SMTP utilisé par Apache SOAP. Téléchargez ensuite Java Beans Activation Framework (JAF), également disponible sur le serveur Sun http://java.sun.com/products/beans/glasgow/jaf.html . Basé sur l'hypothèse que Xerces ou un autre analyseur XML est déjà installé et prêt à l'emploi.

Note: Assurez-vous que votre analyseur XML est conforme à JAXP et utilise l'espace de noms correct. Votre analyseur répond probablement à ces exigences. Si vous rencontrez des problèmes, il est préférable de revenir à Xerces.

Note: Utiliser dernières versions Xerces. La version 1.4 et supérieure fera l'affaire. Il existe un certain nombre de bogues lorsque vous travaillez avec SOAP et Xerces 1.3(.1), je vous conseille donc de ne pas utiliser cette combinaison.

Décompressez les packages JavaMail et JAF, puis incluez-les fichier jar s à votre classpath ainsi qu'à la bibliothèque savon.pot. Chacun de ces fichiers jar doit se trouver soit dans le répertoire racine du programme correspondant, soit dans un sous-répertoire /lib. A la fin de votre variable chemin de classe devrait ressembler à ceci :

$ echo $CLASSPATH /javaxml2/soap-2_2/lib/soap.jar:/javaxml2/lib/xerces.jar: /javaxml2/javamail-1.2/mail.jar:/javaxml2/jaf-1.0.1/activation.jar

Pour Windows, cela ressemblera à ceci :

c:\>echo %CLASSPATH% c:\javaxml2\soap-2_2\lib\soap.jar;c:\javaxml2\lib\xerces.jar; c:\javaxml2\javamail-1.2\mail.jar;c:\javaxml2\jaf-1.0.1\activation.jar

Et enfin ajouter le répertoire javaxml2/soap-2_2/ dans ton chemin de classe pour exécuter les exemples SOAP. J'ai décrit la configuration de plusieurs des exemples de ce chapitre.

Serveur

Pour créer un ensemble de composants côté serveur compatible SOAP, vous avez d'abord besoin d'un moteur de servlet. Comme dans les chapitres précédents, j'ai utilisé Apache Tomcat (disponible sur http://jakarta.apache.org/) comme exemple pour ce chapitre. Vous devrez ajouter tout ce dont le client a besoin pour chemin de classe serveur. La façon la plus simple de le faire est de réinitialiser savon.pot, activation.jar Et mail.jar, ainsi que votre analyseur, dans le répertoire des bibliothèques de votre moteur de servlet. Pour Tomcat, il s'agit du répertoire /lib, qui contient les bibliothèques pour le chargement automatique. Si vous souhaitez fournir un support pour les scripts (qui ne sont pas abordés dans ce chapitre, mais trouvés dans les exemples Apache SOAP), vous devez mettre bsf.jar(disponible sur http://oss.software.ibm.com/developerworks/projects/bsf) et js.jar(disponible sur http://www.mozilla.org/rhino/) dans le même répertoire.

Note: Si vous utilisez Xerces avec Tomcat, vous devrez répéter l'astuce que j'ai décrite au chapitre 10. Renommer analyseur.jar V z_parser.jar, UN jaxp.jar V z_jaxp.jar s'assurer que xerces.jar et la version incluse de JAXP est chargée avant tout autre analyseur ou implémentation JAXP.

Ensuite, rechargez votre moteur de servlet et vous serez prêt à écrire des composants de serveur SOAP.

Servlet de routeur et client d'administration

Outre les opérations de base, Apache SOAP comprend un servlet de routeur ainsi qu'un client d'administration. Même si vous n'avez pas l'intention de les utiliser, je vous recommande de les installer pour vérifier que SOAP est correctement installé. Ce processus dépend du moteur de servlet que vous utilisez, je me limiterai donc à décrire le processus d'installation de Tomcat. Les instructions d'installation pour certains autres moteurs de servlet peuvent être trouvées à http://xml.apache.org/soap/docs/index.html .

L'installation sous Tomcat est très simple : il suffit de récupérer le fichier guerre du savon du répertoire soap-2_2/webapps et déposez-le dans un répertoire $TOMCAT_HOME/applications Web- et c'est tout! Pour vérifier l'installation, entrez l'adresse dans le navigateur http://localhost:8080/soap/servlet/rpcrouter. Vous devriez recevoir une réponse similaire à celle illustrée à la Figure 12-2.

Illustration 12-2. Servlet RPC du routeur

Bien que le message ressemble à un message d'erreur, il indique que tout fonctionne correctement. Vous devriez obtenir la même réponse en pointant votre navigateur vers l'adresse du client administrateur : http://localhost:8080/soap/servlet/messagerouter.

Pour terminer le test du serveur et du client, assurez-vous d'avoir suivi toutes les instructions dans leur intégralité. Exécutez ensuite la classe Java suivante, comme indiqué ci-dessous, pour gérer l'URL de votre servlet pour le servlet du routeur RPC :

C:\>java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter list Services déployés :

Vous devriez obtenir une liste vide de services comme indiqué ci-dessus. Si vous recevez des messages, consultez la longue liste d'erreurs possibles disponible sur http://xml.apache.org/soap/docs/trouble/index.html. Il s'agit de la liste la plus complète des problèmes que vous pourriez rencontrer. Si vous obtenez une liste vide, la configuration est terminée et vous êtes prêt à commencer à regarder les exemples de ce chapitre.

Commençons

Il y a trois étapes principales dans l'écriture de tout système basé sur SOAP. Après avoir énuméré, je m'attarderai brièvement sur chacun d'eux:

  • Choix entre les messages SOAP-RPC et SOAP ;
  • Écrire ou accéder à un service SOAP ;
  • Écrire ou accéder à un client SOAP.

La première étape consiste à choisir si vous utiliserez SOAP pour les appels RPC (où la procédure distante est exécutée sur le serveur) ou les messages (où le client envoie simplement des informations au serveur). Je discute de ces processus en détail ci-dessous. Une fois que vous avez pris cette décision, vous devrez accéder ou créer votre propre service. Bien sûr, puisque nous sommes tous des pros de Java, ce chapitre explique comment créer le vôtre. Et enfin, vous devez écrire un client pour ce service, c'est tout !

RPC ou messagerie ?

Votre première tâche n'a rien à voir avec la programmation et est plutôt une tâche de conception. Vous devez choisir si vous utiliserez le service RPC ou les messages. Nous supposerons que vous connaissez bien RPC (par exemple, en lisant l'un des chapitres de mon livre). Le client exécute une procédure distante sur le serveur puis reçoit une réponse. Dans ce scénario, SOAP agit comme un système XML-RPC riche qui offre une meilleure gestion des erreurs et un meilleur transfert des types de données complexes sur le réseau. Vous connaissez déjà ce concept, et puisqu'il est plus facile d'écrire des systèmes RPC en SOAP, je vais commencer par eux. Cet article décrit comment créer un service RPC, un client RPC et mettre le système en action.

Une autre façon dont SOAP fonctionne est basée sur la messagerie. Au lieu d'effectuer des procédures à distance, il est uniquement utilisé pour partager des informations. Comme vous pouvez le deviner, il s'agit d'un outil puissant qui ne nécessite pas que le client connaisse les méthodes individuelles d'un serveur. Elle rend également plus isolée la simulation de systèmes distants en permettant de transmettre des paquets de données (paquets au sens figuré, et non au sens réseau) à d'autres systèmes. Dans le même temps, les autres systèmes n'ont pas besoin de connaître les opérations qui ont été effectuées avec ces données. Ce style est plus complexe que la programmation RPC, je ne l'énumérerai donc pas ici. Vous le trouverez dans mon livre, ainsi que d'autres détails d'interaction interentreprises. Pour commencer, familiarisez-vous avec la programmation SOAP-RPC.

Comme la plupart des problèmes de conception, cette décision vous appartient entièrement. Analysez votre application et essayez de déterminer pourquoi vous avez besoin d'utiliser SOAP. Si vous disposez d'un serveur et d'un ensemble de clients qui exécutent des fonctions commerciales spécifiques à la demande, RPC vous convient mieux. Dans les systèmes complexes où la communication ne se limite pas à l'exécution de fonctions commerciales spécifiques à la demande, les messages SOAP sont de loin préférés.

Service RPC

Maintenant que les formalités sont terminées, il est temps d'agir. Comme vous le savez, en RPC, vous avez besoin de classes dont les méthodes seront exécutées à distance.

Extraits de code

Je vais commencer par examiner les extraits de code du serveur. Ces extraits sont des classes avec des méthodes qui s'exécutent sur des clients RPC. J'ai utilisé le code de mon livre comme exemples. Au lieu d'utiliser des classes simples, j'ai choisi un exemple plus complexe pour démontrer le plus clairement possible les possibilités de SOAP. J'ai donc utilisé la classe CD comme exemple. On définit d'abord l'élément carte pour chaque type de paramètre non standard. Pour l'attribut encodingStyle, au moins dans Apache SOAP 2.2. vous devez spécifier la valeur http://schemas.xmlsoap.org/soap/encoding/ . Il s'agit actuellement du seul encodage pris en charge. Vous devez spécifier l'espace de noms pour le type défini par l'utilisateur, suivi du nom de la classe suivi du préfixe d'espace de noms pour ce type. Dans notre cas, à ces fins, j'ai utilisé un espace de noms fictif et un simple préfixe " X". Ensuite, en utilisant l'attribut TypeJava, définissez le vrai nom de la classe Java (dans ce cas - javaxml2.cd). Et, enfin, curalesil avec des attributs java2XMLClassName Et xml2JavaClassName. Ils définissent une classe qui convertit de Java en XML et vice versa. J'ai utilisé la classe BeanSerializer incroyablement pratique, également incluse avec Apache SOAP. Si votre paramètre personnalisé est au format JavaBean, ce sérialiseur et ce désérialiseur vous éviteront d'avoir à écrire le vôtre. Vous avez besoin d'une classe avec un constructeur par défaut (rappelez-vous, j'ai donné à la classe CD un constructeur simple et sans paramètre) et publiez toutes les données de cette classe à l'aide de méthodes setXXX Et obtenir XXX. Depuis la classe CD répond parfaitement à toutes ces exigences, BeanSerializerBeanSerializer fonctionne parfaitement.

Note: Quelle classe CD est conforme aux exigences BeanSerializerBeanSerializer. n'a pas beaucoup d'importance. La plupart des classes sont facilement converties dans ce format. Par conséquent, je vous conseille d'éviter d'écrire vos propres sérialiseurs et désérialiseurs. C'est un casse-tête supplémentaire (rien de compliqué, mais trop laborieux) et je vous recommande d'économiser quelques efforts et d'utiliser la conversion de bean dans vos paramètres utilisateur. Dans de nombreux cas, les conversions de bean nécessitent uniquement que vous ayez un constructeur par défaut (pas de paramètres) dans votre classe.

Recréons maintenant pot déposer et ré-héberger notre service :

(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter xml/CDCatalogDD.xml

Attention: Si vous laissez votre moteur de servlet en cours d'exécution et que vous réhébergez le service en même temps, vous devrez redémarrer le moteur de servlet pour activer les nouvelles classes pour le service SOAP et réhéberger le service.

Il ne reste plus qu'à modifier le client pour utiliser les nouvelles classes et méthodes. L'exemple 12-10 contient une version modifiée de la classe client CDAdder. Les modifications apportées à la version précédente sont mises en surbrillance.

Exemple 12-10 : Classe CDAdder mise à jour

package javaxml2 ; importer java.net.URL ; importer java.util.Vector ; importer org.apache.soap.Constants ; import org.apache.soap.Fault ; importer org.apache.soap.SOAPException ; importer org.apache.soap.encoding.SOAPMappingRegistry ; importer org.apache.soap.encoding.soapenc.BeanSerializer ; importer org.apache.soap.rpc.Call ; import org.apache.soap.rpc.Paramètre ; import org.apache.soap.rpc.Response ; importer org.apache.soap.util.xml.QName ; public class CDAdder( public void add(URL url, String title, String artist, String label) lève SOAPException ( System.out.println("Ajouter un CD avec le titre "" + titre + "" par artiste "" + artiste + "" studio " + label); CD cd = nouveau CD(titre, artiste, label); // Crée un objet d'appel Call Call call = new Call(); call.setSOAPMappingRegistry(registre); call.setTargetObjectURI("urn:cd-catalog"); call.setMethodName("addCD"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); // Définition des paramètres Vector params = new Vector(); params.addElement(nouveau Paramètre("cd", CD.class, cd, null)); appeler setParams(params); // Gérer la réponse de réponse d'appel d'invocation ; réponse = call.invoke(url, ""); if (!response.generatedFault()) ( System.out.println("L'ajout du CD s'est terminé avec succès."); ) else ( Fault fault = response.getFault(); System.out.println(Error: " + fault.getFaultString ()); ) ) public static void main(String args) ( if (args.length != 4) ( System.out.println("Template: java javaxml2.CDAdder " + "\"[Titre du CD]\" \"[Nom de l'artiste]\ " \"[CD Studio]\""); retour; ) try ( // URL du serveur SOAP pour se connecter à l'URL url = new URL(args); // Récupère les valeurs du nouveau CD String title = args; Artiste de chaîne = args ; Étiquette de chaîne = args ; // Ajouter le CD CDAdder adder = new CDAdder(); adder.add(url, titre, artiste, label); ) catch (Exception e) ( e.printStackTrace(); ) ) )

Le seul changement vraiment intéressant concerne le mappage de classe. CD:

// Mappez ce type pour qu'il puisse être utilisé avec SOAP SOAPMappingRegistry register = new SOAPMappingRegistry(); Sérialiseur BeanSerializer = new BeanSerializer(); register.mapTypes(Constants.NS_URI_SOAP_ENC, new QName("urn:cd-catalog-demo", "cd"), CD.class, serializer, serializer);

C'est ainsi qu'un paramètre utilisateur peut être encodé et transmis sur le réseau. J'ai déjà dit comment la classe BeanSerializerBeanSerializer peut être utilisé pour traiter des paramètres au format JavaBean, comme une classe CD. J'ai utilisé un descripteur de déploiement pour les spécifier au serveur, mais maintenant je dois dire au client d'utiliser ce sérialiseur et ce désérialiseur. Cette fonction est réalisée par la classe Registre de mappage SOAP. Méthode mapTypes() prend la chaîne cryptée (encore une fois, il est préférable d'utiliser la constante NS_URI_SOAP_ENC) et des informations sur le type de paramètre pour lequel une sérialisation spéciale doit être utilisée. QName est spécifié en premier. C'est pourquoi un espace de noms étrange a été utilisé dans le descripteur de placement. Vous devez fournir le même URN ici, ainsi que le nom local de l'élément (pour cet exemple, "CD"), puis l'objet Java classe classe à sérialiser ( cd.classe) et enfin une instance de la classe à sérialiser et désérialiser. Pour cet exemple, dans les deux cas, l'instance apparaîtra BeanSerializerBeanSerializer. Une fois tous ces paramètres entrés dans le registre, informez-en l'objet appel en utilisant la méthode setSOAPMapping-Registre().

Vous pouvez exécuter cette classe comme indiqué précédemment en ajoutant un CD et tout devrait fonctionner comme prévu :

C:\javaxml2\build>java javaxml2.CDAdder http://localhost:8080/soap/servlet/rpcrouter "Tony Rice" "Manzanita" "Sugar Hill" Ajout du CD intitulé "Tony Rice" par "Manzanita" par Sugar Hill Studio Ajout réussi du CD.

J'ai laissé la modification de classe CDLister pour toi. Tout est fait selon le même modèle. Pour vous tester, vous pouvez vous référer aux fichiers d'exemple de mon livre, qui contiennent déjà ces classes mises à jour.

Remarque : Vous pouvez décider cela parce que la classe CDLister n'interagit pas directement avec l'objet CD(renvoyé par la méthode liste() le type compte Table de hachage) alors vous n'avez pas besoin d'apporter de modifications. Cependant, la classe de retour Table de hachage contient des instances d'objet CD. Si SOAP ne sait pas comment les désérialiser, le client émettra un message d'erreur. Dans ce cas, pour résoudre le problème, vous devez spécifier dans l'objet appel exemple Registre de mappage SOAP.

Traitement efficace des erreurs

Maintenant que vous avez vu les objets utilisateur, effectué des appels RPC, etc., permettez-moi de parler d'un sujet moins excitant : la gestion des erreurs. Dans toute transaction réseau, de nombreux échecs peuvent survenir. Le service ne démarre pas, une erreur dans le fonctionnement du serveur, un objet introuvable, des classes manquantes et bien d'autres problèmes. Pour l'instant j'ai juste utilisé la méthode faute.getString() pour générer des messages d'erreur. Mais cette méthode n'est pas toujours utile. Pour le voir en action, décommentez-le dans le constructeur CDCatalog:

public CDCatalog() ( //catalog = new Hashtable(); // Créer un répertoire addCD(nouveau CD("Nickel Creek", "Nickel Creek", "Sugar Hill")); addCD(nouveau CD("Let it Fall", "Sean Watkins", "Sugar Hill")); addCD(nouveau CD("Aerial Boundaries", "Michael Hedges", "Windham Hill")); addCD(nouveau CD("Taproot", "Michael Hedges", "Windham Hill")); )

Recompilez-le, redémarrez le moteur de servlet et réhébergez-le. Cela entraînera une exception. NullPointerException lorsque le constructeur de classe essaie d'ajouter un CD à non initialisé Table de hachage. Au démarrage du client, un message d'erreur apparaîtra, mais il ne sera pas très informatif :

(gandalf)/javaxml2/build$ java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcrouter Afficher le répertoire actuel du CD. Erreur : Impossible de résoudre objet cible(Impossible de résoudre l'objet cible) : null

Ce n'est pas le genre d'information qui peut aider à détecter et à corriger une erreur. Néanmoins, le framework fait un bon travail de gestion des erreurs. Vous souvenez-vous DOMFaultListenerDOMFaultListener, que vous définissez comme valeur de l'élément faultListener? Il est temps pour lui d'entrer dans le jeu. Renvoyer l'objet en cas d'erreur Défaut contient DOM ( modèle d'objet document) org.w3c.dom.Element Avec des informations détailléesà propos d'une erreur. Tout d'abord, ajoutez une instruction d'importation à votre code source java.util.Iterator:

importer java.net.URL ; importer java.util.énumération ; importer java.util.Hashtable ; importer java.util.Iterator ; importer java.util.Vector ; importer org.apache.soap.Constants ; import org.apache.soap.Fault ; importer org.apache.soap.SOAPException ; importer org.apache.soap.encoding.SOAPMappingRegistry ; importer org.apache.soap.encoding.soapenc.BeanSerializer ; importer org.apache.soap.rpc.Call ; import org.apache.soap.rpc.Paramètre ; import org.apache.soap.rpc.Response ; importer org.apache.soap.util.xml.QName ;

Apportons maintenant des modifications pour gérer les erreurs dans la méthode list() :

if (!response.generatedFault()) ( Paramètre returnValue = response.getReturnValue(); Hashtable catalog = (Hashtable)returnValue.getValue(); Enumeration e = catalog.keys(); while (e.hasMoreElements()) ( String title = (String)e.nextElement(); CD cd = (CD)catalog.get(title); System.out.println(" "" + cd.getTitle() + "" artiste " + cd.getArtist() + " studios " + cd.getLabel()); ) ) else ( Fault fault = response.getFault(); System.out.println("Error: " + fault.getFaultString()); Entrées vectorielles = fault.getDetailEntries(); for (Iterator i = entrées.iterator(); i.hasNext();) ( org.w3c.dom.Element entry = (org.w3c.dom.Element)i.next(); System.out.println(entry .getFirstChild().getNodeValue()); ) )

Utiliser la méthode getDetailEntries() vous accédez au service SOAP sauvegardé et au serveur de données brutes, avec des informations sur le problème. Le code les retraite (généralement il n'y a qu'un seul élément, mais cela nécessite une attention particulière) et intercepte le DOM élément Le contenu de chaque entrée. Essentiellement, voici le XML avec lequel vous travaillez :

SOAP-ENV:Server.BadTargetObjectURI Impossible de résoudre la cible : null Voici ce que nous voulons!

En d'autres termes, l'objet Fault vous donne accès à la partie de l'enveloppe SOAP qui contient des erreurs. De plus, Apache SOAP fournit une trace de pile Java lorsque des erreurs se produisent, fournissant des informations détaillées nécessaires pour les corriger. Intercepter un élément trace de la pile et impression de la valeur du nœud Texteà partir de cet élément votre client peut imprimer la trace de la pile du serveur. Après avoir compilé ces modifications et redémarré le client, vous obtiendrez le résultat suivant :

C:\javaxml2\build>java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcr external Afficher le répertoire actuel du CD. Erreur : Impossible de résoudre la cible : null java.lang.NullPointerException à javaxml2.CDCatalog.addCD(CDCatalog.java:24) à javaxml2.CDCatalog. (CDCatalog.java:14) à java.lang.Class.newInstance0 (méthode native) à java.lang.Class.newInstance (Class.java:237)

Ce n'est pas beaucoup mieux, mais au moins vous pouvez voir la friandise qu'une exception s'est produite. NullPointerException et même trouver les numéros de ligne dans les classes de serveur qui ont ce problème. Le résultat de ces dernières modifications vous a donné une représentation visuelle du problème de gestion des erreurs. Vous devez maintenant vérifier si vos classes de serveur contiennent des erreurs. Oui, j'ai failli oublier, avant ça, n'oublie pas de changer de classe CDCatalog pour se débarrasser des erreurs que nous avons intentionnellement introduites pour plus de clarté !

  1. On parle beaucoup de l'exécution de SOAP sur d'autres protocoles comme SMTP (ou même Jabber). Jusqu'à présent, la norme SOAP ne prévoit pas cela, mais de telles fonctionnalités pourraient être ajoutées à l'avenir. Par conséquent, ne soyez pas surpris si vous rencontrez des discussions actives sur ce sujet.

Qu'est-ce que le SAVON ?

SOAP signifie Simple Object Access Protocol (Simple Object Access Protocol). J'espère qu'après avoir lu l'article vous ne serez que perplexe : "Quel est ce nom étrange ?"

SOAP dans sa forme actuelle est une méthode d'appel de procédure distante (RPC) sur un réseau. (Oui, il est également utilisé pour transmettre des documents au format XML, mais nous allons ignorer cela pour le moment.)

Essayons de comprendre. Imaginez que vous ayez un service qui renvoie une cotation boursière pour un ticker donné (symbole boursier). Il envoie des données au site Nasdaq et génère en fonction du code HTML renvoyé résultat désiré. De plus, afin de permettre à d'autres développeurs de l'utiliser dans leurs applications, vous faites de ce service un composant qui trouve des informations sur les devis via Internet. Cela fonctionne très bien jusqu'au jour où le Nasdaq modifie la mise en page de ses pages. Vous devez revoir toute la logique du composant et envoyer des mises à jour à tous les développeurs qui l'utilisent. Et eux, à leur tour, doivent envoyer des mises à jour à tous leurs utilisateurs. Si cela se produit plus ou moins régulièrement, vous pouvez vous faire beaucoup d'ennemis parmi vos collègues développeurs. Et avec les programmeurs, comme vous le savez, les blagues sont mauvaises. Vous ne voulez pas sortir une photo de votre chat préféré de la déchiqueteuse de bureau demain, n'est-ce pas ?

Ce qu'il faut faire? Voyons voir... tout ce dont vous avez besoin est de fournir une fonction qui prendra un ticker (de type string) en entrée et renverra une cotation boursière (de type float ou double). Ne serait-il donc pas plus simple de laisser vos développeurs appeler cette fonction d'une manière ou d'une autre sur le Web ? Super! C'est aussi nouveau pour moi, il y a COM et Corba, et Java, qui font ça depuis des années... ce qui est vrai est vrai, mais ces méthodes ne sont pas sans défauts. La configuration COM à distance n'est pas triviale. De plus, vous devez ouvrir tellement de ports dans le pare-feu que vous ne pouvez pas économiser assez de bière pour l'administrateur système. Oui, et il faut oublier les utilisateurs de tous systèmes d'exploitation sauf Windows. Mais après tout, les utilisateurs de Linux sont aussi parfois intéressés par l'échange.

Même s'il semble que tout n'est pas perdu pour Utilisateurs Linux s'ils utilisent DCOM, plus ici : http://www.idevresource.com/com/library/res/articles/comonlinux.asp.

Je ne peux pas dire grand-chose sur Corba et Java, donc comme exercice, je suggère aux lecteurs de trouver les inconvénients de ces approches.

SOAP est un standard qui permet de décrire un tel appel distant et la forme sous laquelle le résultat sera renvoyé. Ainsi, vous devez héberger votre fonction dans une application disponible sur le réseau et recevoir des appels sous forme de paquets SOAP. Après cela, vous validez l'entrée, exécutez votre fonction et renvoyez le résultat dans un nouveau package SOAP. L'ensemble du processus peut s'exécuter sur HTTP, vous n'avez donc pas besoin d'ouvrir un tas de ports dans le pare-feu. Est-ce simple ?

De quoi parle l'article

Ceci est le premier d'une série d'articles sur SOAP que nous écrivons chez Agni Software. Dans cet article, je vais essayer de vous donner une idée de ce qu'est SOAP et comment écrire une application qui communique avec un serveur SOAP.

Savon et XML

Si SOAP vous semble toujours simple, ajoutons XML. Maintenant, au lieu d'un nom de fonction et de paramètres, nous obtenons une enveloppe XML plutôt complexe, comme si elle était conçue pour vous embrouiller. Mais n'ayez pas peur. Il y a plus à venir, et vous devez avoir une vue d'ensemble pour apprécier la complexité de SOAP.
Si vous ne savez pas ce qu'est XML, lisez d'abord mon article XML ici : http://www.agnisoft.com/white_papers/xml_delphi.asp.

Tous les packages SOAP ont Format XML. Qu'est-ce que ça veut dire? Voyons. Jetez un oeil à cette fonction (Pascal):
fonction GetStockQuote(Symbole : chaîne) : double ; Ça a l'air bien, mais le problème c'est que c'est Pascal. A quoi sert cette simple définition pour un développeur Java ? Ou pour quelqu'un qui travaille avec VB? Nous avons besoin de quelque chose que tout le monde puisse comprendre, même les programmeurs VB. Donnez-leur donc du XML contenant les mêmes informations (paramètres, cotations boursières, etc.). Vous créez un package SOAP, qui est essentiellement un appel à votre fonction, enveloppé en XML afin que n'importe quelle application sur n'importe quelle plate-forme puisse le comprendre. Voyons maintenant à quoi ressemble notre appel SOAP :
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">


IBM


Informatif, non ? SOAP se simplifie sous nos yeux. Bon, blague à part. Maintenant je vais essayer de vous expliquer comment comprendre cet appel SOAP.

Décryptage des balises

La première balise qui attire votre attention est . Cette balise est l'enveloppe externe du package SOAP, contenant quelques déclarations d'espace de noms qui nous intéressent peu, mais qui sont très importantes pour tout langage de programmation ou parseur. Les espaces de noms sont définis de manière à ce que les préfixes suivants tels que "SOAP-ENV :" ou "xsd :" soient acceptés par l'analyseur.

La balise suivante est . (Nous avons omis une balise non représentée ici - . Ce n'est pas dans cet exemple particulier, mais si vous voulez en savoir plus à ce sujet, consultez la spécification SOAP ici : http://www.w3.org/TR/SOAP/). Étiqueter contient en fait un appel SOAP.

La balise suivante dans la liste est − . Le nom de la balise, GetStockQuote, est la fonction à appeler. Selon la terminologie SOAP, cela s'appelle une opération. GetStockQuote est donc l'opération à effectuer. ns1 est l'espace de noms pointant vers urn:xmethods-quotes dans notre cas.

Remarque concernant les espaces de noms : un espace de noms permet de qualifier une balise XML. Vous ne pouvez pas, par exemple, avoir deux variables portant le même nom dans la même procédure, mais si elles sont dans deux procédures différentes, il n'y a pas de problème. Ainsi, une procédure est un espace de noms, puisque tous les noms qu'elle contient sont uniques. De même, les balises XML sont incluses dans les espaces de noms, donc étant donné un espace de noms et un nom de balise, on peut l'identifier de manière unique. Nous allons définir un espace de noms en tant qu'URI pour distinguer notre NS1 de ses imitateurs. Dans l'exemple ci-dessus, NS1 est un alias pointant vers urn:xmethods-quotes.

Notez également l'attribut encodingStyle - cet attribut spécifie comment l'appel SOAP est sérialisé.

À l'intérieur de l'étiquette contient des paramètres. Dans notre cas le plus simple, nous n'avons qu'un seul paramètre - la balise . Remarquez cette ligne à côté de la balise :
xsi:type="xsd:string"
C'est à peu près ainsi que XML définit les types. (Notez avec quelle intelligence j'ai utilisé le mot « approximativement » lorsque je généralise sur la technologie, ce qui peut changer une fois l'article publié.) Qu'est-ce que cela signifie exactement : le type défini dans l'espace de noms xsi, que vous remarquez est défini dans la balise – xsd:chaîne. Et ceci, à son tour, est une chaîne définie dans l'espace de noms xsd, à nouveau défini précédemment. (Je suis sûr que les avocats seraient ravis de tout cela).

À l'intérieur de l'étiquette "IBM" est répertorié. Il s'agit de la valeur du paramètre de symbole de la fonction GetStockQuote.

Bon, au final, comme des gens bien, on a fermé toutes les balises.

Nous avons donc trouvé le package SOAP qui définit l'appel au serveur SOAP. Et le serveur SOAP, utilisant des analyseurs XML, un bouton rouge et la station spatiale MIR, décode cet appel et détermine que vous avez besoin d'une cotation boursière. Il trouve immédiatement le devis recherché et vous le renvoie sous cette forme :
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>


34.5


Après avoir déballé l'enveloppe SOAP, arraché les rubans et froissé l'emballage, nous apprenons que le prix d'une action IBM est de 34,5.

La plupart des serveurs commerciaux renverraient beaucoup plus d'informations, comme dans quelle devise et à quel prix la dernière action a été achetée. Et le prix de l'action, peut-être, serait plus précis.

De cette façon, nous savons ce que le serveur SOAP attend et ce qu'il renverra. Alors, COMMENT envoyez-vous ces informations ? Tous les transports peuvent être utilisés. Le plus éclairé est HTTP. Je ne rentrerai pas dans les détails du HTTP, pour ceux qui ne connaissent pas, c'est ce que votre navigateur utilise pour communiquer avec les sites que vous visitez.

La requête HTTP souhaitée ressemblerait à ceci :
POST /StockQuote HTTP/1.1
Hébergeur : www.stockquoteserver.com

Longueur du contenu : nnnn
SOAPAction : "Some-URI"

Le paquet de demande de savon ici... La seule autre chose à noter est l'en-tête SOAPAction. Cet en-tête indique l'objet de la demande et est obligatoire. Chaque serveur SOAP peut avoir un nombre illimité de fonctions et peut utiliser l'en-tête SOAPAction pour déterminer quelle fonction est appelée. Les pare-feu et les multiplexeurs peuvent également filtrer le contenu en fonction de cet en-tête.

Réponse SOAP de Serveurs HTTP ressemblera à ceci :
HTTP/1.1 200 OK
Type de contenu : texte/xml ; jeu de caractères="utf-8"
Longueur du contenu : nnnn

Paquet de réponse savon ici... Pourquoi HTTP ? Tout d'abord, les administrateurs réseau n'ont pas besoin d'ouvrir de nombreux ports séparés pour les appels SOAP... le serveur Web peut gérer les appels en toute tranquillité, car Le port 80 est généralement ouvert à tous pour recevoir les demandes entrantes. Un autre avantage est l'extensibilité des serveurs Web utilisant CGI, ISAPI et d'autres modules natifs. Cette extensibilité vous permet d'écrire un module qui gère les requêtes SOAP sans affecter les autres contenus Web.

C'est tout

J'espère que cet article a aidé à faire la lumière sur SOAP. Si vous êtes toujours là et que vous souhaitez en savoir plus sur ce sujet, visitez le site de l'auteur : http://www.agnisoft.com/soap

chapeaux Juillet 23, 2013 à 01:09 pm

Ecrire une application SOAP client-serveur en PHP

  • PHP
  • Didacticiel

Salut tout le monde!
Il se trouva qu'en Dernièrement J'ai commencé à développer des services Web. Mais aujourd'hui, le sujet n'est pas sur moi, mais sur la façon dont nous pouvons écrire notre propre service Web XML basé sur le protocole SOAP 1.2.

J'espère qu'après avoir lu le sujet, vous pourrez :

  • écrivez votre propre implémentation de serveur d'une application Web ;
  • écrire votre propre implémentation client d'une application Web ;
  • rédigez votre propre description de service Web (WSDL);
  • envoyer des tableaux du même type de données au serveur par le client.
Comme vous pouvez le deviner, toute la magie se fera en utilisant PHP et les classes intégrées SoapClient et SoapServer. En tant que lapin, nous aurons un service d'envoi de messages SMS.

1 Énoncé du problème

1.1 Frontières

Au début, je propose de traiter du résultat que nous obtiendrons à la fin du sujet. Comme il a été annoncé ci-dessus, nous allons écrire un service d'envoi de messages sms, et plus précisément, nous allons recevoir des messages de différentes sources en utilisant le protocole SOAP. Après cela, nous examinerons sous quelle forme ils arrivent sur le serveur. Le processus de mise en file d'attente des messages pour un envoi ultérieur au fournisseur dépasse malheureusement le cadre de cet article pour de nombreuses raisons.

1.2 Quelles données seront modifiées ?

D'accord, nous avons les limites ! La prochaine étape qui doit être faite est de décider quelles données nous allons échanger entre le serveur et le client. Sur ce sujet, je vous propose de ne pas être plus sage pendant longtemps et de répondre immédiatement aux principales questions par vous-même :
  • Quelles données minimales doivent être envoyées au serveur pour envoyer un message SMS à un abonné ?
  • Quelle est la quantité minimale de données qui doit être envoyée depuis le serveur pour satisfaire les besoins du client ?
Quelque chose me dit que pour cela il faut envoyer ce qui suit :
  • numéro de téléphone portable, et
  • Texte SMS.
En principe, ces deux caractéristiques suffisent à envoyer, mais il me semble tout de suite qu'un sms de vœux d'anniversaire vous parvient à 3 heures du matin, ou 4 ! En ce moment, je serai très reconnaissant à tout le monde de ne pas m'avoir oublié ! Par conséquent, nous enverrons également au serveur et
  • la date d'envoi du SMS.
La prochaine chose que je voudrais envoyer au serveur est
  • Type de message.
Ce paramètre est facultatif, mais il peut nous être très utile si nous avons rapidement besoin de dire au patron combien de nos clients nous avons « ravis » de notre actualité, et aussi tirer de belles statistiques à ce sujet.

Et pourtant, j'ai oublié quelque chose ! Si l'on réfléchit un peu plus, il est intéressant de noter que le client peut envoyer un message SMS au serveur à la fois, ou un certain nombre d'entre eux. En d'autres termes, dans un paquet de données, il peut y avoir de un à une infinité de messages.

En conséquence, nous obtenons que pour envoyer un message SMS, nous avons besoin des données suivantes :

  • Numéro de téléphone portable,
  • texte sms,
  • le moment de l'envoi d'un SMS à un abonné,
  • type de message.

Nous avons répondu à la première question, maintenant il faut répondre à la deuxième question. Et peut-être que je me permettrai de tricher un peu. Par conséquent, depuis le serveur, nous n'enverrons que des données booléennes dont la valeur a la signification suivante :

  • TRUE - le paquet a atteint avec succès le serveur, passé l'authentification et mis en file d'attente pour l'envoi au fournisseur de SMS
  • FAUX - dans tous les autres cas

Ceci conclut la description de l'énoncé du problème ! Et enfin, passons à la partie la plus intéressante - nous découvrirons quel genre de bête extravagante est ce SOAP !

2 Qu'est-ce que le SAVON ?

En général, au départ, je n'avais pas prévu d'écrire quoi que ce soit sur ce qu'est SOAP et je voulais me limiter aux liens vers le site w3.org avec les spécifications nécessaires, ainsi qu'aux liens vers Wikipedia. Mais à la toute fin, j'ai décidé d'écrire une courte référence sur ce protocole.

Et je commencerai mon histoire par le fait que ce protocole d'échange de données appartient à un sous-ensemble de protocoles basés sur le paradigme dit RPC (Remote Procedure Call) dont l'antipode est REST (Representational State Transfer, transfert d'état représentatif). Vous pouvez en savoir plus à ce sujet sur Wikipedia, les liens vers les articles sont à la toute fin du sujet. De ces articles, nous devons comprendre ce qui suit : "L'approche RPC vous permet d'utiliser un petit nombre de ressources réseau avec un grand nombre de méthodes et un protocole complexe. Avec une approche REST, le nombre de méthodes et la complexité du protocole sont fortement limités, ce qui peut conduire à un grand nombre de ressources individuelles. Autrement dit, par rapport à nous, cela signifie que sur le site dans le cas de l'approche RPC, il y aura toujours une entrée (lien) vers le service et quelle procédure appeler pour traiter les données entrantes que nous transmettons avec les données, tandis qu'avec l'approche REST sur notre Le site comporte de nombreuses entrées (liens), dont chacune n'accepte et ne traite que certaines données. Si quelqu'un qui lit sait comment expliquer la différence entre ces approches encore plus facilement, alors assurez-vous d'écrire dans les commentaires !

La prochaine chose que nous devons savoir à propos de SOAP est que ce protocole utilise le même XML qu'un transport, ce qui, d'une part, est très bon, car. notre arsenal intègre d'emblée toute la puissance de la pile de technologies basées sur ce langage de balisage, à savoir XML-Schema, un langage de description de la structure d'un document XML (merci Wikipédia !), qui permet la validation automatique des données reçues par le serveur de la clientèle.

Et donc, nous savons maintenant que SOAP est le protocole utilisé pour implémenter l'appel de procédure à distance et qu'il utilise XML comme moyen de transport ! Si vous lisez l'article sur Wikipedia, à partir de là, vous pouvez également apprendre qu'il peut être utilisé sur n'importe quel protocole de couche d'application, et pas seulement associé à HTTP (malheureusement, dans ce sujet, nous ne considérerons que SOAP sur HTTP). Et tu sais ce que j'aime le plus dans tout ça ? S'il n'y a pas de suppositions, alors je donnerai un indice - SAVON! ... Quoi qu'il en soit, aucune supposition n'est apparue? ... Avez-vous vraiment lu l'article sur Wikipedia? ... En général, je ne vous tourmenterai pas davantage. Par conséquent, je vais immédiatement passer à la réponse: "SOAP (de l'anglais. Simple Object Access Protocol - un simple protocole accès aux objets; jusqu'à la spécification 1.2)". Le point fort de cette ligne est en italique ! Je ne sais pas quelles conclusions vous avez tirées de tout cela, mais je vois ce qui suit - puisque ce protocole ne peut en aucun cas être qualifié de «simple» (et apparemment même w3 est d'accord avec cela), alors depuis la version 1.2, il a cessé d'être décrypté du tout ! Et il est devenu connu sous le nom de SOAP, juste SOAP et point final.

Bon, d'accord, je vous demande pardon, j'ai un peu dérapé sur le côté. Comme je l'ai écrit précédemment, XML est utilisé comme moyen de transport et les paquets qui circulent entre le client et le serveur sont appelés enveloppes SOAP. Si nous considérons la structure généralisée de l'enveloppe, elle vous semblera très familière, car ressemble à la structure d'une page HTML. Il a une section principale - Envelopper, qui comprend des sections entête Et Corps, ou Défaut. DANS Corps les données sont transmises et il s'agit d'une section obligatoire de l'enveloppe, tandis que entête est facultatif. DANS entête l'autorisation peut être transmise, ou toute autre donnée qui n'est pas directement liée aux données d'entrée des procédures du service Web. Pro Défaut il n'y a rien de spécial à dire, sauf qu'il vient au client du serveur en cas d'erreur.

C'est là que mon histoire générale sur le protocole SOAP se termine (nous examinerons les enveloppes elles-mêmes et leur structure plus en détail lorsque notre client et notre serveur apprendront enfin comment les faire fonctionner l'un dans l'autre) et une nouvelle commence - à propos d'un compagnon SOAP appelé WSDLName(Langage de description des services Web). Oui, oui, c'est la chose même qui effraie la plupart d'entre nous de la tentative même de prendre et de mettre en œuvre notre API sur ce protocole. En conséquence, nous réinventons généralement notre roue avec JSON comme moyen de transport. Alors, qu'est-ce que WSDL ? WSDL est un langage de description de services Web et d'accès à ceux-ci, basé sur le langage XML (c) Wikipedia. Si à partir de cette définition, toute la signification sacrée de cette technologie ne vous apparaît pas clairement, alors j'essaierai de la décrire avec mes propres mots !

Le WSDL est conçu pour permettre à nos clients de communiquer normalement avec le serveur. Pour ce faire, les informations suivantes sont décrites dans le fichier avec l'extension « *.wsdl » :

  • Quels espaces de noms ont été utilisés,
  • Quels schémas de données ont été utilisés,
  • Quels types de messages le service Web attend des clients,
  • Quelles données appartiennent à quelles procédures de service Web,
  • Quelles procédures le service Web contient-il,
  • Comment le client doit-il appeler les procédures du service web,
  • À quelle adresse les appels des clients doivent être envoyés.
Comme vous pouvez le voir, ce fichier est l'intégralité du service Web. En spécifiant l'adresse du fichier WSDL dans le client, nous saurons tout sur n'importe quel service web ! Par conséquent, nous n'avons pas besoin de savoir absolument quoi que ce soit sur l'emplacement du service Web lui-même. Il suffit de connaître l'emplacement de son fichier WSDL ! Bientôt, nous découvrirons que SOAP n'est pas aussi effrayant qu'il est peint (c) proverbe russe.

3 Introduction au schéma XML

Maintenant, nous en savons beaucoup sur ce qu'est SOAP, ce qu'il contient, et nous avons un aperçu du type de pile technologique qui l'entoure. Étant donné que, tout d'abord, SOAP est une méthode d'interaction entre un client et un serveur, et que le langage de balisage XML est utilisé comme moyen de transport pour celui-ci, dans cette section, nous allons comprendre un peu comment la validation automatique des données se produit via des schémas XML.

La tâche principale du schéma est de décrire la structure des données que nous allons traiter. Toutes les données des schémas XML sont divisées en simple(scalaire) et complexe(structures) types. Les types simples incluent des types tels que :

  • doubler,
  • nombre,
  • booléen,
  • date de.
Quelque chose de très simple qui n'a pas d'extensions à l'intérieur. Leur antipode est des types complexes complexes. L'exemple le plus simple d'un type complexe qui vient à l'esprit de tout le monde est celui des objets. Par exemple, un livre. Le livre se compose de propriétés: auteur, Nom, prix, Numéro ISBN etc. Et ces propriétés, à leur tour, peuvent être à la fois des types simples et des types complexes. Et la tâche du schéma XML est de le décrire.

Je propose de ne pas aller bien loin et d'écrire un schéma XML pour notre message sms ! Ci-dessous la description xml du message SMS :

71239876543 Message d'essai 2013-07-20T12:00:00 12
Notre schéma de type complexe ressemblera à ceci :


Cette entrée se lit comme suit : nous avons une variable " message" taper " message" et il existe un type complexe nommé " message", qui consiste en un ensemble séquentiel d'éléments " téléphone" taper chaîne, « texte" taper chaîne, « date" taper dateHeure, « taper" taper décimal. Ces types sont simples et sont déjà définis dans la définition du schéma. Toutes nos félicitations! Nous venons d'écrire notre premier schéma XML !

Je pense que la signification des éléments " élément" Et " typecomplexe» tout est devenu plus ou moins clair pour vous, nous n'allons donc plus nous concentrer dessus et passer immédiatement à l'élément compositeur « séquence". Lorsque nous utilisons l'élément compositeur " séquence» nous vous informons que les éléments qui y sont inclus doivent toujours être dans l'ordre indiqué dans le schéma, et aussi tous sont obligatoires. Mais ne désespérez pas ! Il existe deux autres éléments composer dans les schémas XML : choix" Et " tous". Compositeur choix" indique qu'il devrait y avoir un des éléments qui y sont listés, et le compositeur " tous» – toute combinaison des éléments énumérés.

Comme vous vous en souvenez, dans la première section du sujet, nous avons convenu que le forfait peut être transmis de un à l'infini des messages sms. Je propose donc de comprendre comment de telles données sont déclarées dans le schéma XML. Structure générale le paquet pourrait ressembler à ceci :

71239876543 Message d'essai 1 2013-07-20T12:00:00 12 71239876543 Message d'essai N 2013-07-20T12:00:00 12
Le schéma d'un type aussi complexe ressemblerait à ceci :


Le premier bloc contient la déclaration familière du type complexe " message". Si vous remarquez, alors dans chaque type simple inclus dans " message", de nouveaux attributs qualificatifs ont été ajoutés" minOccurs" Et " maxOccurs". Comme il n'est pas difficile de deviner d'après le nom, le premier ( minOccurs) indique que la séquence donnée doit contenir au moins un élément de type " téléphone», « texte», « date" Et " taper», tandis que le suivant ( maxOccurs) nous déclare qu'il y a au plus un tel élément dans notre séquence. Par conséquent, lorsque nous écrivons nos schémas pour n'importe quelles données, nous avons le choix le plus large dans la façon de les configurer !

Le deuxième bloc du schéma déclare l'élément " liste de messages" taper " Liste de messages". Il est clair que " Liste de messages' est un type complexe qui comprend au moins un élément ' message", Mais nombre maximal de tels éléments n'est pas limité !

4 Écrire votre WSDL

Vous souvenez-vous que WSDL est notre service Web ? J'espère que vous vous souvenez ! Au moment où nous l'écrivons, notre petit service Web flottera dessus. Je vous conseille donc de ne pas tricher.

En général, pour que tout fonctionne correctement pour nous, nous devons transférer un fichier WSDL avec le bon type MIME au client. Pour ce faire, vous devez configurer votre serveur Web en conséquence, à savoir définir le type MIME pour les fichiers avec l'extension *.wsdl sur la ligne suivante :

Application/wsdl+xml
Mais en pratique, j'envoyais généralement l'en-tête HTTP via PHP " texte/xml»:

Header("Content-Type : text/xml; charset=utf-8");
et tout a bien fonctionné !

Je tiens à vous avertir tout de suite, notre service web simple aura une description assez impressionnante, alors ne vous inquiétez pas, car. la plupart du texte est de l'eau obligatoire et une fois écrit, il peut être constamment copié d'un service Web à un autre !

Puisque WSDL est XML, alors dans la toute première ligne, vous devez écrire directement à ce sujet. L'élément racine d'un fichier doit toujours être nommé " définitions»:


Habituellement, WSDL se compose de 4 à 5 blocs principaux. Le tout premier bloc est la définition d'un service web, ou en d'autres termes, un point d'entrée.


Il est dit ici que nous avons un service appelé - " Service SMS". En principe, tous les noms du fichier WSDL peuvent être modifiés par vous comme vous le souhaitez, car ils ne jouent absolument aucun rôle.

Après cela, nous déclarons que dans notre service Web " Service SMS" il y a un point d'entrée ("port"), qui s'appelle " SMSServicePort". C'est à ce point d'entrée que toutes les requêtes des clients vers le serveur seront envoyées. Et on précise dans l'élément " adresse» un lien vers un fichier de gestionnaire qui acceptera les requêtes.

Après avoir défini un service Web et spécifié un point d'entrée pour celui-ci, nous devons lui lier les procédures prises en charge :


Pour ce faire, il liste quelles opérations et sous quelle forme y seront appelées. Ceux. pour le port SMSServicePort» une reliure nommée « Liaison SMSService", qui a le type d'appel " RPC” et HTTP est utilisé comme protocole de transfert (transport). Ainsi, nous avons indiqué ici que nous ferons un appel RPC via HTTP. Après cela, nous décrivons quelles procédures ( opération) sont pris en charge dans le service Web. Nous ne soutiendrons qu'une seule procédure - " envoyer un SMS". Grâce à cette procédure, nos merveilleux messages seront envoyés au serveur ! Une fois la procédure déclarée, il est nécessaire d'indiquer sous quelle forme les données seront transmises. Dans ce cas, il est précisé que des enveloppes SOAP standards seront utilisées.

Après cela, nous devons lier la procédure aux messages :


Pour cela, nous précisons que notre reliure ("binding") est de type" SMSServicePortType" et dans l'élément " portType» avec le même nom de type, spécifiez la liaison des procédures aux messages. Et ainsi, le message entrant (du client vers le serveur) s'appellera " envoyerSmsDemande", et sortant (du serveur vers le client)" envoyerSmsRéponse". Comme tous les noms dans le WSDL, les noms des messages entrants et sortants sont arbitraires.

Maintenant, nous devons décrire les messages eux-mêmes, c'est-à-dire entrant et sortant :


Pour ce faire, on ajoute les éléments " message» avec des noms « envoyerSmsDemande" Et " envoyerSmsRéponse" respectivement. En eux, nous indiquons qu'une enveloppe doit venir à l'entrée, dont la structure correspond au type de données " Demande". Après cela, une enveloppe contenant le type de données est renvoyée par le serveur - " Réponse».

Maintenant, nous devons faire juste un peu - ajouter une description de ces types à notre fichier WSDL ! Et comment pensez-vous que le WSDL décrit les données entrantes et sortantes ? Je pense que vous avez tout compris depuis longtemps et que vous vous êtes dit cela à l'aide de schémas XML ! Et vous aurez tout à fait raison !


Vous pouvez nous féliciter ! Notre premier WSDL a été écrit ! Et nous sommes un peu plus près d'atteindre notre objectif.
Ensuite, nous traiterons de ce que PHP nous apporte pour développer nos propres applications distribuées.

5 Notre premier serveur SOAP

Plus tôt, j'ai écrit que pour créer un serveur SOAP en PHP, nous utiliserons la classe intégrée SoapServer. Pour que toutes les autres actions se déroulent de la même manière que la mienne, vous devrez modifier un peu votre PHP. Pour être encore plus précis, vous devez vous assurer que l'extension "php-soap" est installée. La meilleure façon de le mettre sur votre serveur Web est de le lire sur le site Web officiel de PHP (voir les références).

Une fois que tout a été installé et configuré, nous devrons créer le fichier " service sms.php» avec le contenu suivant :

setClass("SoapSmsGateWay"); //Démarrer le serveur $server->handle();
Ce qui est au-dessus de la ligne avec la fonction "ini_set", j'espère, n'a pas besoin d'être expliqué. Parce que il définit les en-têtes HTTP que nous enverrons du serveur au client et configure l'environnement. Dans la ligne "ini_set", nous désactivons la mise en cache du fichier WSDL afin que nos modifications prennent immédiatement effet sur le client.

Nous arrivons maintenant au serveur ! Comme vous pouvez le voir, le serveur SOAP entier ne fait que trois lignes ! Dans la première ligne, nous créons une nouvelle instance de l'objet SoapServer et transmettons l'adresse de notre description de service Web WSDL à son constructeur. Nous savons maintenant qu'il sera situé à la racine de l'hébergement dans un fichier au nom éloquent " smsservice.wsdl.php". Dans la deuxième ligne, nous indiquons au serveur SOAP quelle classe extraire afin de traiter l'enveloppe reçue du client et renvoyons l'enveloppe avec la réponse. Comme vous l'avez peut-être deviné, c'est dans cette classe que notre seule méthode sera décrite. envoyer un SMS. Dans la troisième ligne, nous démarrons le serveur ! Tout, notre serveur est prêt ! Avec quoi je nous félicite tous!

Nous devons maintenant créer un fichier WSDL. Pour ce faire, vous pouvez soit simplement copier son contenu de la section précédente, soit prendre des libertés et le "modèler" un peu :

"; ?> /" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http:// schemas.xmlsoap.org/wsdl/http/" name="SmsWsdl" xmlns="http://schemas.xmlsoap.org/wsdl/"> /"> /servicesms.php" />
A ce stade, le serveur résultant devrait nous convenir complètement, car. nous pouvons enregistrer les enveloppes qui lui parviennent puis analyser sereinement les données entrantes. Pour que nous puissions recevoir quoi que ce soit sur le serveur, nous avons besoin d'un client. Alors continuons avec eux !

6 Client SOAP en route

Tout d'abord, nous devons créer un fichier dans lequel nous écrirons le client. Comme d'habitude, nous allons le créer à la racine de l'hôte et l'appeler " client.php", et à l'intérieur nous écrivons ce qui suit :

liste de messages = nouvelle liste de messages(); $req->messageList->message = nouveau Message(); $req->messageList->message->phone = "79871234567" ; $req->messageList->message->text = "Tester le message 1" ; $req->messageList->message->date = "2013-07-21T15:00:00.26" ; $req->messageList->message->type = 15 ; $client = new SoapClient("http://($_SERVER["HTTP_HOST"])/smsservice.wsdl.php", array("soap_version" => SOAP_1_2)); var_dump($client->envoiSms($req));
Décrivons nos objets. Lorsque nous avons écrit le WSDL, trois entités y étaient décrites pour l'enveloppe entrant dans le serveur : Demande, Liste de messages Et message. En conséquence, les cours Demande, Liste de messages Et message sont des reflets de ces entités dans notre script PHP.

Après avoir défini les objets, nous devons créer un objet ( $req), qui sera envoyé au serveur. Viennent ensuite les deux lignes les plus chères pour nous ! Notre client SOAP ! Croyez-le ou non, mais cela suffit pour que notre serveur commence à envoyer des messages du client, et aussi pour que notre serveur les reçoive et les traite avec succès ! Dans le premier d'entre eux, nous créons une instance de la classe SoapClient et transmettons l'adresse de l'emplacement du fichier WSDL à son constructeur, et indiquons explicitement dans les paramètres que nous allons travailler en utilisant le protocole SOAP version 1.2. À la ligne suivante, nous appelons la méthode envoyer un SMS objet $ client et afficher immédiatement le résultat dans le navigateur.
Exécutons-le et voyons ce que nous avons finalement obtenu !

J'ai reçu l'objet suivant du serveur :

Objet(stdClass) public "status" => booléen vrai
Et c'est merveilleux, parce que. maintenant, nous savons avec certitude que notre serveur fonctionne et pas seulement, mais peut également renvoyer certaines valeurs au client !

Regardons maintenant le log que nous conservons prudemment côté serveur ! Dans la première partie, nous voyons les données brutes qui sont entrées sur le serveur :

79871234567 Message d'essai 1 2013-07-21T15:00:00.26 15
C'est l'enveloppe. Maintenant vous savez à quoi ça ressemble ! Mais il est peu probable que nous soyons intéressés à l'admirer constamment, alors désérialisons l'objet du fichier journal et voyons si tout va bien pour nous :

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1 " (longueur=37) public "date" => chaîne "2013-07-21T15:00:00.26" (longueur=22) public "type" => chaîne "15" (longueur=2)
Comme vous pouvez le voir, l'objet a été correctement désérialisé, ce dont je tiens à nous féliciter tous ! Ensuite, quelque chose de plus intéressant nous attend ! A savoir, nous enverrons par le client au serveur non pas un message sms, mais tout un pack (pour être plus précis, trois entiers) !

7 Envoi d'objets complexes

Réfléchissons à la façon dont nous pouvons envoyer tout un tas de messages au serveur dans un seul paquet ? Le moyen le plus simple serait probablement d'organiser un tableau à l'intérieur de l'élément messageList ! Faisons-le:

// crée un objet à envoyer au serveur $req = new Request(); $req->messageList = nouvelle MessageList(); $msg1 = nouveau Message(); $msg1->téléphone = "79871234567" ; $msg1->text = "Tester le message 1" ; $msg1->date = "2013-07-21T15:00:00.26" ; $msg1->type = 15 ; $msg2 = nouveau Message(); $msg2->téléphone = "79871234567" ; $msg2->text = "Tester le message 2" ; $msg2->date = "2014-08-22T16:01:10" ; $msg2->type = 16 ; $msg3 = nouveau Message(); $msg3->téléphone = "79871234567" ; $msg3->text = "Tester le message 3" ; $msg3->date = "2014-08-22T16:01:10" ; $msg3->type = 17 ; $req->messageList->message = $msg1 ; $req->messageList->message = $msg2 ; $req->messageList->message = $msg3 ;
Nos journaux indiquent que le paquet suivant provient du client :

79871234567 Message d'essai 1 2013-07-21T15:00:00.26 15 79871234567 Message d'essai 2 2014-08-22T16:01:10 16 79871234567 Message d'essai 3 2014-08-22T16:01:10 17
Quelle bêtise, me direz-vous ? Et vous aurez raison dans un sens, parce que. au moment même où nous avons appris quel objet quittait le client, il est arrivé à notre serveur sous la forme d'une enveloppe exactement de la même forme. Certes, les messages SMS n'étaient pas sérialisés en XML de la manière dont nous avions besoin - ils devaient être enveloppés dans des éléments message, pas dedans Structure. Voyons maintenant sous quelle forme un tel objet vient à la méthode envoyer un SMS:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "Struct" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (longueur=11) public "text" => chaîne "Message de test 1" (longueur=37) public "date" => chaîne "2013-07-21T15:00:00.26" (longueur=22) public " type" => string "15" (length=2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length= 37) public "date" => chaîne "2014-08-22T16:01:10" (longueur=19) public "type" => chaîne "16" (longueur=2) 2 => objet(stdClass) public "téléphone " => string "79871234567" (length=11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length= 19) public "type" => chaîne "17" (longueur=2)
Que nous apporte cette connaissance ? Seulement que le chemin que nous avons choisi n'est pas correct et nous n'avons pas reçu de réponse à la question - "Comment pouvons-nous accéder au serveur structure correcte données?" Mais je suggère de ne pas désespérer et d'essayer de convertir notre tableau en type un objet:

$req->messageList->message = (objet)$req->messageList->message ;
Dans ce cas, nous recevrons une autre enveloppe :

79871234567 Message d'essai 1 2013-07-21T15:00:00.26 15 79871234567 Message d'essai 2 2014-08-22T16:01:10 16 79871234567 Message d'essai 3 2014-08-22T16:01:10 17
Entré dans la méthode envoyer un SMS l'objet a la structure suivante :

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "BOGUS" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (longueur=11) public "text" => chaîne "Message de test 1" (longueur=37) public "date" => chaîne "2013-07-21T15:00:00.26" (longueur=22) public " type" => string "15" (length=2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length= 37) public "date" => chaîne "2014-08-22T16:01:10" (longueur=19) public "type" => chaîne "16" (longueur=2) 2 => objet(stdClass) public "téléphone " => string "79871234567" (length=11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length= 19) public "type" => chaîne "17" (longueur=2)
Quant à moi, alors « d'un changement des places des termes, la somme ne change pas » (c). Quoi FAUX, Quoi Structure Nous n'avons pas encore atteint notre objectif ! Et pour y parvenir, nous devons nous assurer qu'au lieu de ces noms incompréhensibles, notre message. Mais comment y parvenir, l'auteur ne le sait pas encore. Par conséquent, la seule chose que nous puissions faire est de nous débarrasser du conteneur supplémentaire. En d'autres termes, nous allons maintenant nous assurer qu'au lieu de message devenu FAUX! Pour ce faire, modifiez l'objet comme suit :

// crée un objet à envoyer au serveur $req = new Request(); $msg1 = nouveau Message(); $msg1->téléphone = "79871234567" ; $msg1->text = "Tester le message 1" ; $msg1->date = "2013-07-21T15:00:00.26" ; $msg1->type = 15 ; $msg2 = nouveau Message(); $msg2->téléphone = "79871234567" ; $msg2->text = "Tester le message 2" ; $msg2->date = "2014-08-22T16:01:10" ; $msg2->type = 16 ; $msg3 = nouveau Message(); $msg3->téléphone = "79871234567" ; $msg3->text = "Tester le message 3" ; $msg3->date = "2014-08-22T16:01:10" ; $msg3->type = 17 ; $req->messageList = $msg1 ; $req->messageList = $msg2 ; $req->messageList = $msg3 ; $req->messageList = (objet)$req->messageList ;
Et si nous avons de la chance et que le nom correct ressort du schéma ? Pour ce faire, regardons l'enveloppe qui est arrivée :

79871234567 Message d'essai 1 2013-07-21T15:00:00.26 15 79871234567 Message d'essai 2 2014-08-22T16:01:10 16 79871234567 Message d'essai 3 2014-08-22T16:01:10 17
Oui, le miracle ne s'est pas produit ! FAUX- nous ne gagnerons pas ! Entré envoyer un SMS l'objet dans ce cas ressemblera à ceci:

Object(stdClass) public "messageList" => object(stdClass) public "BOGUS" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public " text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public "type" => string "15" (longueur =2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length=37) public "date" => string " 2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone" => string "79871234567" (length= 11) public "text" => string "Message test 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string " 17" (longueur=2)
Comme on dit - "Presque" ! Sur cette note (un peu triste), je propose de conclure tranquillement et de tirer quelques conclusions pour nous-mêmes.

8Conclusion

Enfin nous sommes arrivés ici! Décidons ce que vous pouvez faire maintenant :
  • vous pouvez écrire le fichier WSDL nécessaire pour votre service Web ;
  • vous pouvez écrire votre propre client sans aucun problème qui peut communiquer avec le serveur en utilisant le protocole SOAP ;
  • vous pouvez écrire votre propre serveur communiquer avec le monde extérieur via SOAP ;
  • vous pouvez envoyer des tableaux du même type d'objets au serveur depuis votre client (avec certaines restrictions).
Aussi, nous avons fait quelques découvertes par nous-mêmes lors de nos petites recherches :
  • la classe native SoapClient ne sait pas sérialiser correctement des structures de données de même type en XML ;
  • lors de la sérialisation d'un tableau en XML, il produit élément supplémentaire Avec nom Structure;
  • lors de la sérialisation d'un objet en XML, il crée un élément supplémentaire nommé FAUX;
  • FAUX moindre mal que Structure du fait que l'enveloppe est plus compacte (aucun espace de noms supplémentaire n'est ajouté dans l'en-tête XML de l'enveloppe) ;
  • malheureusement, la classe SoapServer ne valide pas automatiquement les données d'enveloppe avec notre schéma XML (peut-être que d'autres serveurs ne le font pas non plus).
Vous avez aimé l'article ? Partager avec des amis: