SOAP en webservices. Simple Object Access Protocol (SOAP) Algemene beschrijving

De titel van het onderwerp is eigenlijk een vraag, aangezien Ik weet zelf niet wat het is en voor het eerst ga ik er in het kader van dit artikel mee aan de slag. Het enige dat ik kan garanderen dat de onderstaande code zal werken, maar mijn zinnen zullen slechts aannames en gissingen zijn over hoe ik dit zelf allemaal begrijp. Dus laten we gaan ...

Invoering

We moeten beginnen met waar het concept van webservices voor is gemaakt. Tegen de tijd dat dit concept verscheen, bestonden er al technologieën in de wereld waarmee applicaties op afstand konden interageren, waarbij het ene programma een methode in een ander programma kon aanroepen, dat op een computer in een andere stad of zelfs een land kon worden uitgevoerd. Dit alles wordt afgekort als RPC (Remote Procedure Calling). Voorbeelden zijn onder meer CORBA-technologieën en voor Java - RMI (Remote Method Invoking). En alles lijkt goed in hen te zijn, vooral in CORBA, tk. je kunt er in elke programmeertaal mee werken, maar er ontbrak nog iets. Ik geloof dat het nadeel van CORBA is dat het via enkele van zijn eigen netwerkprotocollen werkt in plaats van eenvoudig HTTP, dat door elke firewall zal kruipen. Het idee achter de webservice was om een ​​RPC te maken die in HTTP-pakketten zou blijven. Zo begon de ontwikkeling van de standaard. Wat zijn de basisconcepten van deze norm:
  1. ZEEP... Voordat u een procedure op afstand aanroept, moet u deze aanroep beschrijven in een SOAP XML-bestand. SOAP is gewoon een van de vele XML-markeringen die in webservices worden gebruikt. Alles wat we ergens via HTTP naar toe willen sturen, wordt eerst omgezet in een XML SOAP-beschrijving, daarna wordt het in een HTTP-pakket gestopt en via TCP/IP naar een andere computer op het netwerk gestuurd.
  2. WSDL... Er is een webservice, d.w.z. een programma waarvan de methoden op afstand kunnen worden aangeroepen. Maar de norm vereist dat er een beschrijving aan dit programma wordt toegevoegd, die zegt: "ja, je vergist je niet - dit is echt een webservice en je kunt er die en die methoden van oproepen." Deze beschrijving wordt weergegeven door een ander XML-bestand dat een ander formaat heeft, namelijk WSDL. Die. WSDL is slechts een XML-beschrijvingsbestand voor een webservice en niets anders.
Waarom vraag je het zo kort? Kun je niet meer details krijgen? Waarschijnlijk wel, maar hiervoor moet je je wenden tot boeken als Mashnin T. "Java Web Services". Daar staat voor de eerste 200 pagina's een gedetailleerde beschrijving van elke tag van de SOAP- en WSDL-standaarden. Zal ik het doen? Naar mijn mening, nee, tk. dit alles wordt automatisch gegenereerd in Java, en het enige wat u hoeft te doen is de inhoud te schrijven van de methoden die op afstand zouden moeten worden aangeroepen. Dus in Java was er zo'n API als JAX-RPC. Als iemand het niet weet, als ze zeggen dat Java die en die API heeft, betekent dit dat er een pakket is met een reeks klassen die de technologie in kwestie inkapselen. JAX-RPC evolueerde lange tijd van versie naar versie en evolueerde uiteindelijk naar JAX-WS. WS staat duidelijk voor WebService en je zou kunnen denken dat dit tegenwoordig een simpele hernoeming van RPC in een populair woord is. Dit is niet het geval, aangezien Nu zijn webservices afgestapt van het oorspronkelijke idee en kunt u niet alleen externe methoden aanroepen, maar ook eenvoudig documentberichten in SOAP-indeling verzenden. Waarom dit nodig is, weet ik nog niet, het antwoord is waarschijnlijk niet "voor het geval dat het plotseling nodig zal zijn". Zelf zou ik graag willen leren van meer ervaren kameraden. En als laatste was er nog JAX-RS voor de zogenaamde RESTful webservices, maar dit is een onderwerp voor een apart artikel. Op dit punt kan de introductie worden beëindigd, omdat: daarna gaan we leren werken met JAX-WS.

Algemene benadering

Webservices hebben altijd een client en een server. De server is onze webservice en wordt soms een endpoint genoemd (zoals het endpoint waar SOAP-berichten van de client naartoe gaan). We moeten het volgende doen:
  1. Beschrijf de interface van onze webservice
  2. Implementeer deze interface
  3. Start onze webservice
  4. Schrijf een client en bel de vereiste webservicemethode op afstand
Een webservice kan op verschillende manieren worden gestart: ofwel door een klasse te beschrijven met een hoofdmethode en de webservice rechtstreeks te starten, als een server, of door deze te implementeren op een server zoals Tomcat of een andere. In het tweede geval starten we zelf geen nieuwe server en openen we geen andere poort op de computer, maar vertellen we gewoon aan de Tomcat-servletcontainer dat "we de webserviceklassen hier hebben geschreven, ze alstublieft publiceren, zodat iedereen die contact opneemt u kunt gebruik maken van de webservice ". Ongeacht de methode om de webservice te lanceren, we zullen dezelfde klant hebben.

Server

Laten we IDEA starten en een nieuw project maken Nieuw project maken... Laten we een naam invoeren HalloWebService en druk op de knop Volgende, dan de knop Finish... In map src maak een pakket aan ru.javarush.ws... In dit pakket zullen we de HelloWebService-interface maken: pakket ru. javarush. ww; // dit zijn annotaties, d.w.z. een manier om onze klassen en methoden te markeren, // in verband met webservicetechnologie javax importeren. jws. WebMethode; javax importeren. jws. Webservice; javax importeren. jws. zeep. SOAPBinding; // we zeggen dat onze interface zal werken als een webservice@WebService // zeg dat de webservice zal worden gebruikt om methoden aan te roepen@SOAPBinding (stijl = SOAPBinding. Stijl. RPC) openbare interface HelloWebService ( // we zeggen dat deze methode op afstand kan worden aangeroepen@WebMethod public String getHelloString (Stringnaam); ) In deze code zijn de WebService- en WebMethod-klassen zogenaamde annotaties en doen ze niets anders dan onze interface en zijn methode markeren als een webservice. Hetzelfde geldt voor de SOAPBinding-klasse. Het enige verschil is dat SOAPBinding een parameterannotatie is. In dit geval wordt de stijlparameter gebruikt met een waarde die zegt dat de webservice niet via documentberichten werkt, maar als een klassieke RPC, d.w.z. om de methode aan te roepen. Laten we de logica van onze interface implementeren en een HelloWebServiceImpl-klasse in ons pakket maken. Overigens merk ik op dat het einde van de klasse in Impl een conventie in Java is, volgens welke de implementatie van interfaces op deze manier wordt aangeduid (Impl - van het woord implementatie, d.w.z. implementatie). Dit is geen vereiste en je bent vrij om de klasse te noemen wat je wilt, maar de regels van een goede vorm vereisen het: pakket ru. javarush. ww; // dezelfde annotatie als bij het beschrijven van de interface, javax importeren. jws. Webservice; // maar hier gebruikt met de parameter endpointInterface, // met vermelding van de volledig gekwalificeerde klassenaam van de interface van onze webservice@WebService (endpointInterface = "ru.javarush.ws.HelloWebService") public class HelloWebServiceImpl implementeert HelloWebService (@Override public String getHelloString (String name) ( // stuur gewoon een groet terug retourneer "Hallo," + naam + "!" ; )) Laten we onze webservice starten als een onafhankelijke server, d.w.z. zonder tussenkomst van Tomcat en applicatieservers (dit is een onderwerp voor een aparte discussie). Om dit te doen, in de projectstructuur in de map src laten we een pakket ru.javarush.endpoint maken en daarin een HelloWebServicePublisher-klasse maken met een hoofdmethode: pakket ru. javarush. eindpunt; // class om een ​​webserver met webservices te starten javax importeren. xml. ww. eindpunt; // klasse van onze webservice import ru. javarush. ww. HalloWebServiceImpl; public class HelloWebServicePublisher (public static void main (String... args) ( // start de webserver op poort 1986 // en op het adres dat is opgegeven in het eerste argument, // start de webservice die in het tweede argument is doorgegeven Eindpunt. publiceren ( "http: // localhost: 1986 / wss / hallo", nieuwe HelloWebServiceImpl ()); )) Laten we deze les nu uitvoeren door op . te klikken Shift + F10... Er verschijnt niets in de console, maar de server is actief. U kunt dit verifiëren door in de browser de regel http: // localhost: 1986 / wss / hallo?Wsdl te typen. De pagina die wordt geopend, bewijst enerzijds dat er een webserver (http://) is gestart op onze computer (localhost) op poort 1986 en toont aan de andere kant de WSDL-beschrijving van onze webservice. Als u de applicatie stopt, wordt de beschrijving ontoegankelijk, evenals de webservice zelf, dus we zullen dit niet doen, maar doorgaan met het schrijven van de client.

Klant

In de projectmap src maak een pakket ru.javarush.client, en daarin de HelloWebServiceClient-klasse met de hoofdmethode: pakket ru. javarush. cliënt; // nodig om de wsdl-beschrijving te krijgen en erdoorheen // bereik de webservice zelf java importeren. netto. URL; // zo'n uitvoering zal plaatsvinden bij het werken met een URL-object java importeren. netto. MisvormdeURLUitzondering; // klassen om xml-ku te ontleden met wsdl-beschrijving // en bereik de servicetag erin javax importeren. xml. naamruimte. QNaam; javax importeren. xml. ww. Onderhoud; // de interface van onze webservice (we hebben meer nodig) import ru. javarush. ww. HalloWebService; public class HelloWebServiceClient (public static void main (String args) gooit MalformedURLException ( // maak een link naar de wsdl-beschrijving URL-url = nieuwe url ( "http: // localhost: 1986 / wss / hallo? wsdl") ; // We kijken naar de parameters van de volgende constructor in de allereerste tag van de WSDL-beschrijving - definities // zie het eerste argument in het kenmerk targetNamespace // zie het 2e argument in het naamattribuut QName qname = nieuwe QName ("http: //ws.site/", "HelloWebServiceImplService"); // Nu kunnen we de servicetag in de wsdl-beschrijving bereiken, Serviceservice = Service. creëren (url, qname); // en dan tot aan de geneste poorttag, zodat // krijg een link naar een webservice-object op afstand van ons HalloWebService hallo = service. getPort (HelloWebService.klasse); // Hoera! Nu kunt u de externe methode aanroepen Systeem. uit. println (hallo. getHelloString ("CodeGym")); )) Ik gaf maximale opmerkingen over de code in de aanbieding. Ik heb niets toe te voegen, dus ren (Shift + F10). We zouden de tekst in de console moeten zien: Hallo, CodeGym! Als je het niet hebt gezien, ben je waarschijnlijk vergeten de webservice te starten.

Conclusie

In dit onderwerp werd een korte excursie naar webservices gepresenteerd. Nogmaals, veel van wat ik heb geschreven is mijn giswerk over hoe het werkt, en daarom moet er niet te veel op vertrouwd worden. Ik zou het op prijs stellen als deskundige mensen mij corrigeren, want dan leer ik iets. UPD.

SOAP (Simple Object Access Protocol) is een gestandaardiseerd protocol voor het overbrengen van berichten tussen een client en een server. Het wordt meestal gebruikt in combinatie met HTTP (S), maar het kan ook werken met andere applicatielaagprotocollen (bijvoorbeeld SMTP en FTP).
SOAP-testen vanuit het oogpunt van testtechnieken verschilt niet fundamenteel van het werken met andere API's, maar vereist een voorbereidende voorbereiding (in termen van protocoltheorie) en speciale testtools. In dit artikel zou ik een kleine checklist willen formuleren met de nodige kennis en vaardigheden, die even nuttig zal zijn voor zowel een SOAP-tester (die vaak niet weet "wat hij moet begrijpen" na het stellen van een probleem), als voor een manager die wordt gedwongen de kennis van testers te evalueren en opleidingsplannen te ontwikkelen.

Theoretische basis

Het feit dat SOAP een protocol is, is belangrijk voor het testen: je moet het protocol zelf bestuderen, de "primaire" standaarden en protocollen waarop het is gebaseerd, en (indien nodig) bestaande extensies.

XML
XML is een opmaaktaal die lijkt op HTML. Elk bericht dat via SOAP wordt verzonden / ontvangen, is een XML-document waarin de gegevens overzichtelijk en gemakkelijk leesbaar zijn, bijvoorbeeld:



Julia
Natasha
Herinnering
Vergeet niet een artikel te schrijven!


Meer details over XML zijn te vinden op w3schools of codenet (in het Russisch). Let op de beschrijving van naamruimten (een methode om conflicten op te lossen bij het beschrijven van elementen in XML) - het gebruik ervan is noodzakelijk in SOAP.

XSD
Tijdens het werken is het altijd handig om een ​​gestandaardiseerde beschrijving van mogelijke XML-documenten te hebben en deze op correcte vulling te controleren. Hiervoor is er een XML Schema Definition (of kortweg XSD). De twee belangrijkste kenmerken van XSD voor een tester zijn de beschrijving van datatypes en het opleggen van beperkingen aan mogelijke waarden. Het element uit het vorige voorbeeld kan bijvoorbeeld optioneel worden gemaakt en worden beperkt tot 255 tekens met behulp van XSD:

...







...

SOAP-extensies
U kunt in uw werk ook verschillende SOAP-extensies tegenkomen - standaarden zoals WS-*. Een van de meest voorkomende is WS-Security, waarmee je kunt werken met encryptie en elektronische handtekeningen. Vaak wordt daarbij WS-Policy gebruikt, waarmee u de rechten voor het gebruik van uw dienst kunt beheren.

Voorbeeld met WS-Security:


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

Al deze extensies zijn vrij complexe constructies die niet in elke SOAP-service worden gebruikt; Het is onwaarschijnlijk dat het relevant is om ze in detail te bestuderen in de beginfase van het leren van SOAP-testen.

instrumenten

Zoals je al begreep, is SOAP een serieuze zaak, om ermee te werken moet je de theorie en talloze normen kennen. In de praktijk zou deze complexiteit leiden tot zeer merkbare arbeidskosten (zo zou het nodig zijn om elke keer naar het diagram in een notitieboekje te kijken en verzoeken met curl te verzenden). Daarom zijn er tools in het leven geroepen om het werken met SOAP te vergemakkelijken.

XML / XSD-editors
Een goede tester begint met testen in het stadium van het schrijven van de documentatie, dus het is handig om speciale editors te gebruiken om de circuits te controleren. De twee meest bekende zijn Oxygen (cross-platform) en Altova (alleen Windows); beiden worden betaald. Dit zijn zeer krachtige programma's die analisten actief gebruiken bij het beschrijven van diensten.

In mijn praktijk bleken drie functies van de editors nuttig: XSD-visualisatie, XML-generatie vanuit XSD en XML-validatie tegen XSD.

1. XSD-visualisatie is nodig voor een visuele weergave van het schema, waarmee u snel de vereiste elementen en attributen kunt isoleren, evenals bestaande beperkingen. Voor een CheckTextRequest is bijvoorbeeld het tekstelement vereist en zijn alle drie de attributen optioneel (waarbij het options attribuut standaard op nul staat).

Visualisatie is nodig wanneer er veel typen en beperkingen in het schema zijn. Als je dit gewoon wilt en niet wilt betalen voor aangepaste editors, dan kun je gratis alternatieven overwegen (zoals JDeveloper).

2. XML genereren op basis van XSD handig als u een geldig voorbeeld van een bericht wilt zien. Ik gebruik het om snel te experimenteren met de mogelijke invulling van de boodschap en om de nuances van de werking van restricties te checken.

3. Na gebruik van de functie uit punt 2, is het handig om XML-validatie tegen XSD- dat wil zeggen, controleer het bericht op juistheid. Samen zorgen functies 2 en 3 ervoor dat u lastige defecten in de XSD kunt opsporen, zelfs wanneer de service zelf in ontwikkeling is.

Testtool - SoapUI

SOAP-testen betekent bijna altijd het gebruik van SoapUI. U kunt in verschillende bronnen lezen over het gebruik van deze tool (,), maar de meest effectieve manier is om uzelf vertrouwd te maken met de officiële documentatie. Ik onderscheid 8 voorwaardelijke niveaus van SoapUI-vaardigheid:

Niveau 1 - Ik kan verzoeken verzenden
Leer een op WSDL gebaseerd project te maken. SoapUI kan alle benodigde queries voor je genereren; je hoeft alleen maar de juistheid van hun vulling te controleren en op de knop "Verzenden" te drukken. Als je eenmaal de vaardigheden hebt ontwikkeld om de juiste query's te maken, moet je de kunst beheersen om onjuiste query's te maken die fouten veroorzaken.

Niveau 2 - Ik kan testsuites en testcases doen
Begin met het doen van mini-autotests. Met testsuites en testcases kunt u API-testscripts maken, gegevens voorbereiden op verzoeken en automatisch de ontvangen reactie controleren op wat wordt verwacht. In het begin kunnen ze eenvoudig worden gebruikt als verzamelingen zoekopdrachten. Als u bijvoorbeeld een defect heeft geïntroduceerd en dit snel wilt controleren na reparatie, kunt u speciaal voor defectverzoeken een aparte testkit selecteren.

Niveau 3 - kan beweringen schrijven
Nadat je de testgevallen onder de knie hebt, is het handig om te leren hoe je ze automatisch controleerbaar maakt. Daarna hoef je niet meer met je "ogen" naar informatie over het antwoord te zoeken: als er een automatische controle is, worden de gevallen groen gemarkeerd (als de controle is geslaagd) of rood (als deze niet is geslaagd) . SoapUI biedt een groot aantal mogelijke beweringen, maar de handigste en eenvoudigste zijn Bevat en Bevat niet. Met hun hulp kunt u de aanwezigheid van een specifieke tekst in het ontvangen antwoord controleren. Deze controles ondersteunen ook zoekopdrachten naar reguliere expressies.

Niveau 4 - XPath en/of XQuery gebruiken in Assertions
Voor degenen die een beetje bekend zijn met de gebruikersinterface met Selenium, is XPath een bekend iets. Met XPath kunt u grofweg zoeken naar elementen in een XML-document. XQuery is een vergelijkbare technologie die XPath intern kan gebruiken; deze taal is veel krachtiger, het lijkt op SQL. Beide talen kunnen worden gebruikt in Assertions. Controles met hun hulp zijn gerichter en stabieler, zodat uw zaken meer vertrouwd worden.

Niveau 5 - kan complexe tests schrijven met behulp van speciale stappen

Testgevallen kunnen niet slechts één verzoek bevatten, maar ook meerdere (bijvoorbeeld wanneer u een standaard gebruikersscenario wilt emuleren “maak een entiteit” → “exporteer een entiteit”). Er kunnen andere speciale stappen tussen verzoeken zijn, bijvoorbeeld:

  • Eigenschappen en eigendomsoverdracht (helpen om gegevens opnieuw te gebruiken en over te dragen tussen verzoeken);
  • JDBC Request (gebruikt om gegevens uit de database te halen);
  • Conditional Goto (hiermee kun je branches of loops maken in een testcase);
  • Voer TestCase uit (helpt om enkele typische vragen in afzonderlijke testgevallen te brengen en ze waar nodig aan te roepen).

Niveau 6 - Groovy-scripts gebruiken

Met SoapUI kun je op verschillende plaatsen Groovy-scripts schrijven. Het eenvoudigste geval is het genereren van gegevens in de query zelf met behulp van $ (=) inserts. Ik gebruik altijd inserts zoals deze:

  • $ (= nieuwe datum (). formaat ("jjjj-MM-dd'T'HH: mm: ss"))- om de huidige datum en tijd in het vereiste formaat in te voegen;
  • $ (= java.util.UUID.randomUUID ())- om een ​​correct gevormde willekeurige GUID in te voegen.

Volledige scripts kunnen worden gebruikt als stappen in cases en controles. Op een gegeven moment zul je merken dat verschillende speciale stappen van het vijfde niveau kunnen worden vervangen door één script tegelijk.

Niveau 7 - MockServices gebruiken
SoapUI op basis van WSDL kan mock-objecten genereren. Een mock is de eenvoudigste simulatie van een service. Met behulp van "mocks" kunt u al beginnen met het schrijven en debuggen van testgevallen voordat de service daadwerkelijk beschikbaar is om te testen. Ze kunnen ook worden gebruikt als "stubs" voor tijdelijk niet-beschikbare services.

Niveau 8 - God SoapUI
Je kent het verschil tussen de betaalde en gratis versies van SoapUI en gebruikt de SoapUI API in je code. Je gebruikt plug-ins en voert cases uit via de opdrachtregel en/of CI. Uw testgevallen zijn eenvoudig en gemakkelijk te onderhouden. Over het algemeen "eet je de hond" op dit instrument. Ik zou graag met iemand praten die SoapUI op dit niveau beheerst. Als je zo bent - meld je dan af in de comments!

Testen met programmeertalen

Ik zal een voorbeeld geven van hoe een verzoek aan de YandexSpeller API eruit ziet, gemaakt met groovy-wslite:

import wslite.soap *
def client = nieuwe SOAPClient ("http://speller.yandex.net/services/spellservice?WSDL")
def response = client.send (SOAPAction: "http://speller.yandex.net/services/spellservice/checkText") (
lichaam (
CheckTextRequest ("lang": "ru", "xmlns": "http://speller.yandex.net/services/spellservice") (
tekst ("doorn")
}
}
}
assert "error" == response.CheckTextResponse.SpellResult.error.s.text ()
beweren "1" == [e-mail beveiligd]()

Voor zover ik weet, zijn er nog geen frameworks op hoog niveau (zoals Rest-assured) om SOAP te testen, maar onlangs is er een interessant hulpmiddel verschenen: karate. Het kan worden gebruikt om gevallen voor SOAP- en REST-testen te beschrijven in de vorm van scripts zoals Cucumber / Gherkin. Voor veel testers zal karate een ideale oplossing zijn, omdat dergelijke scenario's in termen van de complexiteit van het schrijven en onderhouden van cases ergens in het midden zullen liggen tussen het gebruik van SoapUI en het schrijven van je eigen SOAP-testraamwerk.

Conclusie

Je zult SOAP waarschijnlijk nooit voor jezelf willen testen (zoals je zou kunnen met REST). Het is een zwaargewicht protocol dat wordt gebruikt in serieuze bedrijfsoplossingen. Maar de zwaarte ervan is tegelijkertijd een geschenk voor de tester: alle gebruikte technologieën zijn gestandaardiseerd, er zijn hoogwaardige tools voor het werk. Het enige dat van de tester wordt gevraagd, is de wil om ze te leren en te gebruiken.

Laten we die checklist met noodzakelijke vaardigheden voor een tester samenstellen. Dus als u net begint met het testen van SOAP-services, moet u het volgende kennen en kunnen gebruiken:

  • WSDL.
  • ZEEP.
  • XML / XSD-editors (op XSD-weergaveniveau).
  • SoapUI op niveau 1.

Zoals je kunt zien, ligt de focus vooral op het bestuderen van standaarden, in SoapUI is het vrij eenvoudig om queries uit te voeren. Terwijl je jezelf onderdompelt in SOAP-testen, zul je worden geconfronteerd met taken die serieuzere vaardigheden en kennis vereisen, maar je moet niet proberen alles in één keer te leren. Consistentie in het verhogen van de complexiteit van de uitgevoerde taken is veel belangrijker. Door deze aanbeveling op te volgen, zul je op een dag beseffen dat je een goede specialist op dit gebied bent geworden!

hoeden 23 juli 2013 om 13:09

Een SOAP client-server applicatie schrijven in PHP

  • PHP
  • zelfstudie

Dag iedereen!
Toevallig ben ik de laatste tijd begonnen met het ontwikkelen van webservices. Maar vandaag gaat het onderwerp niet over mij, maar over hoe we onze XML Web Service kunnen schrijven op basis van het SOAP 1.2-protocol.

Ik hoop dat je na het lezen van het onderwerp in staat zult zijn om zelfstandig:

  • schrijf uw eigen server-side implementatie van een webapplicatie;
  • schrijf uw eigen client-side implementatie van een webapplicatie;
  • schrijf uw eigen webservicebeschrijving (WSDL);
  • door de client-arrays van hetzelfde type gegevens naar de server verzenden.
Zoals je misschien al geraden had, wordt alle magie gedaan met behulp van PHP en de ingebouwde SoapClient- en SoapServer-klassen. De service voor het verzenden van sms zal als een konijn werken.

1 Probleemstelling

1.1 Grenzen

In het begin stel ik voor om het resultaat te behandelen dat we aan het einde van het onderwerp zullen bereiken. Zoals hierboven aangekondigd, zullen we een dienst schrijven voor het verzenden van sms-berichten, of beter gezegd, we zullen berichten van verschillende bronnen ontvangen via het SOAP-protocol. Daarna zullen we bekijken in welke vorm ze naar de server komen. Het proces van het in de wachtrij plaatsen van berichten voor verdere verzending naar de provider valt helaas om vele redenen buiten het bestek van dit bericht.

1.2 Welke gegevens wijzigen wij?

Mooi, we hebben de grenzen bepaald! De volgende stap die moet worden gedaan, is om te beslissen welke gegevens we tussen de server en de client gaan uitwisselen. Over dit onderwerp stel ik voor om lange tijd niet wijs te zijn en onmiddellijk de belangrijkste vragen voor mezelf te beantwoorden:
  • Wat is de minimale hoeveelheid gegevens die naar de server moet worden verzonden om een ​​sms-bericht naar een abonnee te sturen?
  • Wat is de minimale hoeveelheid gegevens die vanaf de server moet worden verzonden om aan de behoeften van de klant te voldoen?
Iets zegt me dat hiervoor het volgende moet worden verzonden:
  • ook mobiel telefoonnummer
  • SMS-tekst.
In principe zijn deze twee kenmerken voldoende om te verzenden, maar ik stel me meteen een geval voor als er een sms met een verjaardagsgroet bij je komt om 3 uur 's nachts, of 4 uur! Op dit moment zal ik iedereen heel dankbaar zijn dat ze mij niet vergeten zijn! Daarom sturen we ook naar de server en
  • de datum van verzending van het sms-bericht.
Het volgende dat ik naar de server wil sturen is
  • Het type bericht.
Deze parameter is optioneel, maar het kan voor ons erg handig zijn als we de baas snel moeten vertellen hoeveel van onze klanten we "gelukkig hebben gemaakt" met ons nieuws, en ook wat mooie statistieken over deze score moeten trekken.

En toch ben ik iets vergeten! Als we wat meer nadenken, dan is het vermeldenswaard dat een klant zowel één sms-bericht als een aantal tegelijk naar de server kan sturen. Met andere woorden, één datapakket kan één tot oneindig veel berichten bevatten.

Als gevolg hiervan krijgen we dat we voor het verzenden van een sms-bericht de volgende gegevens nodig hebben:

  • Telefoon nummer,
  • sms-tekst,
  • tijdstip van het verzenden van een SMS-bericht naar een abonnee,
  • soort bericht.

We hebben de eerste vraag beantwoord, nu is het noodzakelijk om de tweede vraag te beantwoorden. En misschien zal ik mezelf een beetje hacken. Daarom sturen we vanaf de server alleen booleaanse gegevens, waarvan de waarde de volgende betekenis heeft:

  • TRUE - het pakket heeft de server met succes bereikt, is geverifieerd en is in de wachtrij geplaatst om naar de sms-provider te worden verzonden
  • ONWAAR - in alle andere gevallen

Hiermee is de beschrijving van de probleemstelling afgerond! En tot slot, laten we naar het meest interessante gaan - laten we uitzoeken wat een bizar beest deze SOAP is!

2 Waar is SOAP mee bedoeld?

In het algemeen was ik aanvankelijk niet van plan iets te schrijven over wat SOAP is en wilde ik me beperken tot links naar de w3.org-site met de nodige specificaties, evenals links naar Wikipedia. Maar helemaal aan het einde besloot ik een korte referentie over dit protocol te schrijven.

En ik zal mijn verhaal beginnen met het feit dat dit protocol voor gegevensuitwisseling behoort tot een subset van protocollen die gebaseerd zijn op het zogenaamde RPC-paradigma (Remote Procedure Call), waarvan het tegenovergestelde REST (Representational State Transfer) is. U kunt hier meer over lezen op Wikipedia, links naar artikelen staan ​​helemaal aan het einde van het onderwerp. Uit deze artikelen moeten we het volgende begrijpen: “De RPC-benadering stelt u in staat een kleine hoeveelheid netwerkbronnen te gebruiken met een groot aantal methoden en een complex protocol. Bij een REST-aanpak is het aantal methoden en de complexiteit van het protocol strikt beperkt, wat kan leiden tot een groot aantal individuele resources." Dat wil zeggen, voor ons betekent dit dat in het geval van de RPC-aanpak op de site er altijd één invoer (link) naar de service zal zijn en welke procedure moet worden aangeroepen om de binnenkomende gegevens te verwerken die we samen met de gegevens overdragen, terwijl met de REST-aanpak op onze site de site veel invoer (links) heeft, die elk alleen bepaalde gegevens accepteert en verwerkt. Als iemand die leest het verschil in deze benaderingen nog gemakkelijker weet uit te leggen, schrijf dan in de reacties!

Het volgende dat we over SOAP moeten weten, is dat dit protocol dezelfde XML als transport gebruikt, wat aan de ene kant erg goed is, omdat onmiddellijk in ons arsenaal komt alle kracht van de stapel technologieën op basis van deze opmaaktaal in ons arsenaal, namelijk XML-Schema - een taal voor het beschrijven van de structuur van een XML-document (dankzij Wikipedia!), waarmee automatische validatie van gegevens mogelijk is die van clients naar de server komen.

En dus weten we nu dat SOAP een protocol is dat wordt gebruikt om procedureaanroepen op afstand te implementeren en dat XML als transportmiddel gebruikt! Als je het artikel op Wikipedia leest, kun je van daaruit ook leren dat het bovenop elk applicatielaagprotocol kan worden gebruikt, en niet alleen in combinatie met HTTP (helaas zullen we in dit onderwerp alleen SOAP over HTTP beschouwen). En weet je wat ik het leukste vind aan dit alles? Als er geen gissingen zijn, dan zal ik je een hint geven - SOAP! ... Hoe dan ook, er waren geen gissingen? ... Je hebt het artikel op Wikipedia zeker gelezen? ... Over het algemeen zal ik je niet verder martelen . Daarom ga ik meteen naar het antwoord: “SOAP (van het Engelse Simple Object Access Protocol - simple protocol toegang tot objecten; tot specificatie 1.2) ". De meest opmerkelijke dingen aan deze regel zijn cursief gedrukt! Ik weet niet welke conclusies je uit dit alles hebt getrokken, maar ik zie het volgende - aangezien dit protocol niet "eenvoudig" kan worden genoemd (en het hier blijkbaar zelfs in w3) mee eens is, sinds versie 1.2 wordt het op de een of andere manier niet meer ontcijferd ! En het werd bekend als SOAP, gewoon SOAP-periode.

Wel, oké, neem me niet kwalijk, het slipte een beetje opzij. Zoals ik eerder schreef, wordt XML gebruikt als transport, en de pakketten die tussen de client en de server liggen, worden SOAP-enveloppen genoemd. Als we kijken naar de algemene structuur van de envelop, dan zal het je heel bekend voorkomen, omdat lijkt op de structuur van een HTML-pagina. Het heeft een hoofdgedeelte - Omhullen die secties bevat koptekst en Lichaam of Schuld... V Lichaam gegevens worden overgedragen en het is een verplicht onderdeel van de envelop, terwijl koptekst is optioneel. V koptekst autorisatie kan worden verzonden, of andere gegevens die niet direct verband houden met de invoergegevens van de webserviceprocedures. Wat betreft Schuld er valt niets bijzonders te vertellen, behalve dat het bij eventuele fouten van de server naar de client komt.

Dit besluit mijn overzichtsverhaal over het SOAP-protocol (we zullen de enveloppen zelf en hun structuur in meer detail bekijken wanneer onze client en server eindelijk leren hoe ze elkaar kunnen gebruiken) en een nieuwe begint - over de SOAP-compagnon genaamd WSDL(Webservices Beschrijving Taal). Ja, ja, dit is precies wat de meesten van ons afschrikken van de poging om onze API op dit protocol te gebruiken en te implementeren. Hierdoor vinden we met JSON meestal het wiel opnieuw uit voor transport. Dus wat is WSDL? WSDL is een taal voor het beschrijven en openen van webservices, gebaseerd op de XML (c) Wikipedia-taal. Als je vanuit deze definitie niet de hele heilige betekenis van deze technologie begrijpt, dan zal ik proberen het in mijn eigen woorden te beschrijven!

WSDL is zo ontworpen dat onze klanten normaal met de server kunnen communiceren. Hiervoor wordt in het bestand met de extensie "* .wsdl" de volgende informatie beschreven:

  • Welke naamruimten werden gebruikt,
  • Welke dataschema's werden gebruikt,
  • Welke soorten berichten verwacht de webservice van klanten,
  • Welke gegevens horen bij welke procedures van de webservice,
  • Welke procedures bevat de webservice,
  • Hoe de klant de webserviceprocedures moet noemen,
  • Naar welk adres moeten de oproepen van de klant worden gestuurd.
Zoals u kunt zien, is dit bestand de volledige webservice. Door het adres van het WSDL-bestand in de client op te geven, weten we alles over elke webservice! Hierdoor hoeven we helemaal niets te weten over waar de webservice zelf zich bevindt. U hoeft alleen de locatie van het WSDL-bestand te weten! Binnenkort zullen we leren dat SOAP niet zo verschrikkelijk is als het is geschilderd (c) Russische spreekwoorden.

3 Inleiding tot XML-schema

Nu weten we veel over wat SOAP is, wat erin zit, en hebben we een overzicht van de technology stack eromheen. Aangezien SOAP in de eerste plaats een manier van interactie is tussen een client en een server, en XML-opmaaktaal wordt gebruikt als transportmiddel, zullen we in deze sectie een beetje begrijpen hoe gegevens automatisch worden gevalideerd met behulp van XML-schema's.

De hoofdtaak van het schema is het beschrijven van de structuur van de gegevens die we gaan verwerken. Alle gegevens in XML-schema's zijn onderverdeeld in: eenvoudig(scalair) en complex(structuren) typen. Eenvoudige typen omvatten typen zoals:

  • lijn,
  • nummer,
  • booleaanse waarde,
  • datum.
Iets heel eenvoudigs waar geen extensies in zitten. Hun tegenpool is complexe complexe typen. Het eenvoudigste voorbeeld van een complex type dat iedereen kan bedenken, zijn objecten. Bijvoorbeeld een boek. Het boek bestaat uit eigenschappen: auteur, titel, prijs, ISBN-nummer enzovoort. En deze eigenschappen kunnen op hun beurt zowel eenvoudige als complexe zijn. En de taak van het XML-schema is om het te beschrijven.

Ik stel voor dat je niet ver gaat en een XML-schema schrijft voor ons sms-bericht! Hieronder vindt u de xml-beschrijving van het sms-bericht:

71239876543 Test bericht 2013-07-20T12: 00: 00 12
Ons complexe typeschema ziet er als volgt uit:


Dit item luidt als volgt: we hebben een variabele " bericht"Type" Bericht"En er is een complex type genaamd" Bericht", die bestaat uit een opeenvolgende reeks elementen" telefoon"Type snaar, « tekst"Type snaar, « datum"Type datum Tijd, « type"Type decimale... Deze typen zijn eenvoudig en zijn al gedefinieerd in de schemabeschrijving. Gefeliciteerd! We hebben zojuist ons eerste XML-schema geschreven!

Ik denk dat de betekenis van de elementen " element" en " complexType"Voor jou is alles min of meer duidelijk geworden, dus daar gaan we ons niet meer op focussen en gaan direct over op het componisten element" volgorde". Wanneer we het element componist " volgorde»We informeren dat de elementen die erin zijn opgenomen altijd in de volgorde moeten worden geplaatst die in het diagram is aangegeven, en dat ze ook allemaal vereist zijn. Maar wanhoop niet! Er zijn nog twee componistenelementen in XML-schema's: “ keuze" en " alle". componist " keuze"Informeert dat er een van de elementen moet zijn die erin worden vermeld, en de componist" alle"- elke combinatie van de vermelde elementen.

Zoals u zich herinnert, hebben we in het eerste deel van het onderwerp afgesproken dat één tot oneindig sms-berichten in een pakket kunnen worden verzonden. Daarom stel ik voor om te begrijpen hoe dergelijke gegevens in het XML-schema worden gedeclareerd. De algemene structuur van het pakket kan er als volgt uitzien:

71239876543 Testbericht 1 2013-07-20T12: 00: 00 12 71239876543 Testbericht N 2013-07-20T12: 00: 00 12
Het schema voor zo'n complex type ziet er als volgt uit:


Het eerste blok bevat de bekende declaratie van het complexe type “ Bericht". Als je het hebt gemerkt, dan is in elk eenvoudig type opgenomen in " Bericht", Er zijn nieuwe kwalificatiekenmerken toegevoegd" minOccurs" en " maxOccurs". Zoals je uit de naam zou kunnen raden, is de eerste ( minOccurs) geeft aan dat deze reeks ten minste één element van het type “ telefoon», « tekst», « datum" en " type", Terwijl de volgende ( maxOccurs) het attribuut verklaart ons dat er maximaal één van dergelijke elementen in onze reeks is. Als gevolg hiervan krijgen we, wanneer we onze schema's voor gegevens schrijven, de ruimste keuze in hun aanpassing!

Het tweede blok van het diagram verklaart het element " berichtenlijst"Type" Berichtenlijst". Het is duidelijk dat " Berichtenlijst"Is een complex type dat ten minste één element bevat" bericht", Maar het maximale aantal van dergelijke elementen is niet beperkt!

4 Uw eigen WSDL schrijven

Weet u nog dat WSDL onze webservice is? Ik hoop dat je het je herinnert! Terwijl we het schrijven, zal onze kleine webservice erop drijven. Daarom stel ik voor om niet vals te spelen.

Over het algemeen moeten we, om alles correct te laten werken, een WSDL-bestand met het juiste MIME-type naar de klant overbrengen. Om dit te doen, moet u uw webserver dienovereenkomstig configureren, namelijk het MIME-type voor bestanden met de extensie "* .wsdl" op de volgende regel instellen:

Applicatie / wsdl + xml
Maar in de praktijk stuur ik meestal de HTTP-header " tekst / xml»:

Header ("Inhoudstype: tekst / xml; charset = utf-8");
en alles werkte prima!

Ik wil je meteen waarschuwen dat onze eenvoudige webservice een nogal indrukwekkende beschrijving zal hebben, dus schrik niet, want de meeste tekst is verplicht en eenmaal geschreven, kun je constant van de ene webservice naar de andere kopiëren!

Aangezien WSDL XML is, moet u er in de allereerste regel direct over schrijven. Het root-element van het bestand moet altijd de naam " definities»:


Doorgaans bestaat WSDL uit 4-5 hoofdblokken. Het allereerste blok is de definitie van de webservice, of met andere woorden, het toegangspunt.


Er staat hier dat we een dienst hebben genaamd - " SMS-service". Kortom, u kunt alle namen in het WSDL-bestand wijzigen in wat u maar wilt. ze spelen absoluut geen rol.

Daarna kondigen we aan dat in onze webservice “ SMS-service"Er is een ingangspunt ("poort") genaamd" Sms-servicepoort". Het is naar dit ingangspunt dat alle verzoeken van clients naar de server worden verzonden. En we geven aan in het element " adres»Een link naar het handlerbestand dat verzoeken accepteert.

Nadat we een webservice hebben gedefinieerd en er een ingangspunt voor hebben gespecificeerd, moeten we de ondersteunde procedures eraan binden:


Hiervoor wordt vermeld welke bewerkingen en in welke vorm deze zullen worden aangeroepen. Die. voor de haven " Sms-servicepoort"Een binding wordt gedefinieerd onder de naam" SMSServiceBinding", die het type oproep heeft" rpc"En HTTP wordt gebruikt als het transmissieprotocol (transport). We hebben hier dus aangegeven dat we een RPC-aanroep zullen doen via HTTP. Daarna beschrijven we welke procedures ( operatie) worden ondersteund in de webservice. We ondersteunen slechts één procedure - " verstuur sms". Via deze procedure worden onze prachtige berichten naar de server gestuurd! Nadat de procedure is aangekondigd, moet worden aangegeven in welke vorm de gegevens worden verzonden. In dit geval wordt aangegeven dat standaard SOAP-enveloppen worden gebruikt.

Daarna moeten we de procedure aan berichten binden:


Om dit te doen, specificeren we dat onze "binding" van het type is " SmsServicePoortType"En in het element" poorttype»Met de naam van hetzelfde type geven we de binding van procedures aan berichten aan. En dus zal het inkomende bericht (van client naar server) de naam " stuurSmsVerzoek", En uitgaand (van server naar client)" stuurSmsReactie". Zoals alle namen in WSDL zijn de namen van inkomende en uitgaande berichten willekeurig.

Nu moeten we de berichten zelf beschrijven, d.w.z. inkomend en uitgaand:


Om dit te doen, voegen we de elementen " bericht"Met de namen" stuurSmsVerzoek" en " stuurSmsReactie" respectievelijk. Daarin geven we aan dat er een envelop naar de ingang moet komen, waarvan de structuur overeenkomt met het gegevenstype " Verzoek". Daarna retourneert de server een envelop met het gegevenstype - " Antwoord».

Nu moeten we het kleinste ding doen - een beschrijving van deze typen toevoegen aan ons WSDL-bestand! En hoe denkt u dat de WSDL inkomende en uitgaande gegevens beschrijft? Ik denk dat je alles al lang begreep en dat tegen jezelf zei met behulp van XML-schema's! En je zult helemaal gelijk hebben!


Je mag ons feliciteren! Onze eerste WSDL is geschreven! En we zijn een stap dichter bij het bereiken van dit doel.
Vervolgens zullen we kijken naar wat PHP ons biedt om onze eigen gedistribueerde applicaties te ontwikkelen.

5 Onze eerste SOAP-server

Eerder schreef ik dat we de ingebouwde SoapServer-klasse zullen gebruiken om een ​​SOAP-server in PHP te maken. Om ervoor te zorgen dat alle verdere acties op dezelfde manier plaatsvinden als de mijne, moet je je PHP een beetje aanpassen. Meer specifiek moet u ervoor zorgen dat de extensie "php-soap" is geïnstalleerd. Hoe je het op je webserver zet, lees je best op de officiële PHP-site (zie de lijst met referenties).

Nadat alles is geïnstalleerd en geconfigureerd, moeten we een bestand maken in de hoofdmap van uw hosting " smsservice.php"Met de volgende inhoud:

setClass ("SoapSmsGateWay"); // Start de server $ server-> handle ();
Ik hoop dat het niet nodig is om uit te leggen wat er boven de lijn staat met de functie "ini_set". Omdat daar wordt bepaald welke HTTP-headers we van de server naar de client gaan sturen en wordt de omgeving geconfigureerd. In de regel met "ini_set" schakelen we caching van het WSDL-bestand uit, zodat onze wijzigingen daarin onmiddellijk van kracht worden op de client.

Nu komen we bij de server! Zoals je kunt zien, is de hele SOAP-server slechts drie regels lang! In de eerste regel maken we een nieuwe instantie van het SoapServer-object en geven deze het adres van onze WSDL-webservicebeschrijving aan de constructor door. Nu weten we dat het zich in de root van de hosting zal bevinden in een bestand met de voor zichzelf sprekende naam " smsservice.wsdl.php". In de tweede regel vertellen we de SOAP-server welke klasse moet worden opgehaald om de van de klant ontvangen envelop te verwerken en de envelop met het antwoord terug te sturen. Zoals je misschien al geraden had, is het in deze klasse dat onze enige methode zal worden beschreven. verstuur sms... Op de derde regel starten we de server! Dat is alles, onze server is klaar! Waarmee ik ons ​​allemaal feliciteer!

Nu moeten we een WSDL-bestand maken. Om dit te doen, kunt u ofwel eenvoudig de inhoud van de vorige sectie kopiëren, of de vrijheid nemen en het een beetje "template":

"; ?> / "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/ "> /"> /smsservice.php "/>
In dit stadium zou de resulterende server volledig bij ons moeten passen, omdat: we kunnen enveloppen die binnenkomen registreren en vervolgens rustig de binnenkomende gegevens analyseren. Om iets op de server te kunnen ontvangen, hebben we een client nodig. Dus laten we het doen!

6 SOAP-client onderweg

Allereerst moeten we een bestand maken waarin we de klant zullen schrijven. Zoals gewoonlijk zullen we het in de root van de host maken en het een naam geven " client.php", En binnenin schrijven we het volgende:

messageList = nieuwe MessageList (); $ req-> messageList-> bericht = nieuw bericht (); $ req-> messageList-> message-> phone = "79871234567"; $ req-> messageList-> message-> text = "Testbericht 1"; $ req-> messageList-> message-> date = "2013-07-21T15: 00: 00.26"; $ req-> messageList-> bericht-> type = 15; $ client = nieuwe SoapClient ("http: // ($ _SERVER [" HTTP_HOST "]) / smsservice.wsdl.php, array (" soap_version "=> SOAP_1_2)); var_dump ($ client-> sendSms ($ req));
Laten we onze objecten beschrijven. Toen we de WSDL schreven, beschreef het drie entiteiten voor de envelop die de server binnenkwam: Verzoek, Berichtenlijst en Bericht... dienovereenkomstig klassen Verzoek, Berichtenlijst en Bericht zijn reflecties van deze entiteiten in ons PHP-script.

Nadat we de objecten hebben gedefinieerd, moeten we een object maken ( $ vereist), die we naar de server sturen. Daarna zijn er twee meest gekoesterde regels voor ons! Onze SOAP-klant! Geloof het of niet, maar dit is genoeg om berichten van de klant op onze server te laten stromen, en ook om onze server deze met succes te laten ontvangen en verwerken! In de eerste maken we een instantie van de SoapClient-klasse en geven het adres van de locatie van het WSDL-bestand door aan de constructor, en geven expliciet in de parameters aan dat we zullen werken met SOAP versie 1.2. Op de volgende regel noemen we de methode verstuur sms object $ klant en het resultaat onmiddellijk in de browser weergeven.
Laten we het uitvoeren en zien wat we uiteindelijk hebben!

Het volgende object is naar mij teruggestuurd van de server:

Object (stdClass) public "status" => boolean true
En dit is geweldig, want nu weten we zeker dat onze server werkt en niet alleen werkt, maar ook enkele waarden aan de klant kan retourneren!

Laten we nu eens kijken naar het logboek dat we voorzichtig aan de serverkant bewaren! In het eerste deel ervan zien we de onbewerkte gegevens die naar de server zijn gekomen:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15
Dit is de envelop. Nu weet je hoe het eruit ziet! Maar het zal voor ons nauwelijks interessant zijn om het constant te bewonderen, dus laten we het object uit het logbestand deserialiseren en kijken of alles goed met ons is:

Object (stdClass) public "messageList" => object (stdClass) public "message" => object (stdClass) public "phone" => string "79871234567" (lengte = 11) public "text" => string "Testbericht 1 "(lengte = 37) openbaar" datum "=> string" 2013-07-21T15: 00: 00.26 "(lengte = 22) openbaar" type "=> string" 15 "(lengte = 2)
Zoals je kunt zien, is het object correct gedeserialiseerd, waarmee ik ons ​​allemaal wil feliciteren! Vervolgens wacht ons iets interessanters! Namelijk - we sturen door de client niet één sms-bericht naar de server, maar een hele reeks (meer precies, drie)!

7 Complexe objecten verzenden

Laten we eens nadenken over hoe we een hele reeks berichten in één pakket naar de server kunnen sturen? Waarschijnlijk is de gemakkelijkste manier om de array in het messageList-element te ordenen! Laten we dit doen:

// maak een object om naar de server te sturen $ req = new Request (); $ req-> messageList = nieuwe MessageList (); $ msg1 = nieuw bericht (); $ msg1-> telefoon = "79871234567"; $ msg1-> text = "Testbericht 1"; $ msg1-> datum = "2013-07-21T15: 00: 00.26"; $ msg1-> type = 15; $ msg2 = nieuw bericht (); $ msg2-> telefoon = "79871234567"; $ msg2-> text = "Testbericht 2"; $ msg2-> datum = "2014-08-22T16: 01: 10"; $ msg2-> type = 16; $ msg3 = nieuw bericht (); $ msg3-> telefoon = "79871234567"; $ msg3-> text = "Testbericht 3"; $ msg3-> datum = "2014-08-22T16: 01: 10"; $ msg3-> type = 17; $ req-> messageList-> bericht = $ msg1; $ req-> messageList-> bericht = $ msg2; $ req-> messageList-> bericht = $ msg3;
Uit onze logboeken blijkt dat het volgende pakket afkomstig is van een klant:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15 79871234567 Testbericht 2 2014-08-22T16: 01: 10 16 79871234567 Testbericht 3 2014-08-22T16: 01: 10 17
Wat een onzin, zegt u? En in zekere zin heb je gelijk, want we hebben zojuist vernomen welk object de client heeft verlaten, en toen kwam het in absoluut dezelfde vorm naar onze server in de vorm van een envelop. Sms-berichten waren echter niet geserialiseerd naar XML zoals we ze nodig hadden - ze moesten in elementen worden verpakt bericht, niet in structuur... Laten we nu eens kijken in welke vorm zo'n object tot de methode komt verstuur sms:

Object (stdClass) public "messageList" => object (stdClass) public "message" => object (stdClass) public "Struct" => array (grootte = 3) 0 => object (stdClass) public "phone" => string "79871234567" (lengte = 11) openbaar "tekst" => string "Testbericht 1" (lengte = 37) openbaar "datum" => string "2013-07-21T15: 00: 00.26" (lengte = 22) openbaar " typ "=> string" 15 "(length = 2) 1 => object (stdClass) public" phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 2 "(length = 37) public "date" => string "2014-08-22T16: 01: 10" (lengte = 19) public "type" => string "16" (lengte = 2) 2 => object (stdClass) public "phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 3 "(length = 37) public" date "=> string" 2014-08-22T16: 01: 10 "(length = 19) openbaar "type" => string "17" (lengte = 2)
Wat levert deze kennis ons op? Alleen dat het pad dat we hebben gekozen niet correct is en we geen antwoord hebben gekregen op de vraag - "Hoe kunnen we de juiste datastructuur op de server krijgen?" Maar ik stel voor om niet te wanhopen en te proberen onze array naar het type te casten een voorwerp:

$ req-> messageList-> bericht = (object) $ req-> messageList-> bericht;
In dit geval ontvangen we nog een envelop:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15 79871234567 Testbericht 2 2014-08-22T16: 01: 10 16 79871234567 Testbericht 3 2014-08-22T16: 01: 10 17
Kwam in methode verstuur sms het object heeft de volgende structuur:

Object (stdClass) public "messageList" => object (stdClass) public "message" => object (stdClass) public "BOGUS" => array (grootte = 3) 0 => object (stdClass) public "phone" => string "79871234567" (lengte = 11) openbaar "tekst" => string "Testbericht 1" (lengte = 37) openbaar "datum" => string "2013-07-21T15: 00: 00.26" (lengte = 22) openbaar " typ "=> string" 15 "(length = 2) 1 => object (stdClass) public" phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 2 "(length = 37) public "date" => string "2014-08-22T16: 01: 10" (lengte = 19) public "type" => string "16" (lengte = 2) 2 => object (stdClass) public "phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 3 "(length = 37) public" date "=> string" 2014-08-22T16: 01: 10 "(length = 19) openbaar "type" => string "17" (lengte = 2)
Wat mij betreft, dan "van een verandering in de plaatsen van de voorwaarden - de som verandert niet" (c). Wat BOGU, wat structuur- het doel is door ons nog niet bereikt! En om dit te bereiken, moeten we ervoor zorgen dat in plaats van deze onbegrijpelijke namen, onze native wordt weergegeven bericht... Maar hoe dit te bereiken, weet de auteur nog niet. Daarom is het enige wat we kunnen doen de extra container weg te doen. Met andere woorden, we zullen het nu zo maken dat in plaats van: bericht werd BOGU! Laten we hiervoor het object als volgt wijzigen:

// maak een object om naar de server te sturen $ req = new Request (); $ msg1 = nieuw bericht (); $ msg1-> telefoon = "79871234567"; $ msg1-> text = "Testbericht 1"; $ msg1-> datum = "2013-07-21T15: 00: 00.26"; $ msg1-> type = 15; $ msg2 = nieuw bericht (); $ msg2-> telefoon = "79871234567"; $ msg2-> text = "Testbericht 2"; $ msg2-> datum = "2014-08-22T16: 01: 10"; $ msg2-> type = 16; $ msg3 = nieuw bericht (); $ msg3-> telefoon = "79871234567"; $ msg3-> text = "Testbericht 3"; $ msg3-> datum = "2014-08-22T16: 01: 10"; $ msg3-> type = 17; $ req-> messageList = $ msg1; $ req-> messageList = $ msg2; $ req-> messageList = $ msg3; $ req-> messageList = (object) $ req-> messageList;
Wat als we geluk hebben en de juiste naam uit het diagram wordt getrokken? Laten we hiervoor de ontvangen envelop bekijken:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15 79871234567 Testbericht 2 2014-08-22T16: 01: 10 16 79871234567 Testbericht 3 2014-08-22T16: 01: 10 17
Ja, het wonder is niet gebeurd! BOGU- we zullen niet winnen! Kom binnen verstuur sms het object ziet er in dit geval als volgt uit:

Object (stdClass) public "messageList" => object (stdClass) public "BOGUS" => array (size = 3) 0 => object (stdClass) public "phone" => string "79871234567" (length = 11) public " tekst "=> string" Testbericht 1 "(lengte = 37) openbaar" datum "=> tekenreeks" 2013-07-21T15: 00: 00.26 "(lengte = 22) openbaar" type "=> tekenreeks" 15 "(lengte = 2) 1 => object (stdClass) public "phone" => string "79871234567" (lengte = 11) public "text" => string "Testbericht 2" (lengte = 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 "Testbericht 3" (lengte = 37) public "date" => string "2014-08-22T16: 01: 10" (lengte = 19) public "type" => string " 17 "(lengte = 2)
Zoals ze zeggen - "Bijna"! Op deze (een beetje droevige) noot stel ik voor om geleidelijk af te ronden en enkele conclusies voor mezelf te trekken.

8 Conclusie

Eindelijk zijn we hier! Laten we definiëren wat u nu kunt doen:
  • u kunt het vereiste WSDL-bestand voor uw webservice schrijven;
  • je kunt zonder problemen je eigen client schrijven die met de server kan communiceren via het SOAP-protocol;
  • je kunt je eigen server schrijven die via SOAP met de buitenwereld communiceert;
  • je kunt arrays van objecten van hetzelfde type naar de server sturen vanaf je client (met enkele beperkingen).
We hebben ook enkele ontdekkingen voor onszelf gedaan in de loop van ons kleine onderzoek:
  • de native SoapClient-klasse kan datastructuren van hetzelfde type in XML niet correct serialiseren;
  • bij het serialiseren van een array naar XML, wordt een extra element gemaakt met de naam structuur;
  • bij het serialiseren van een object naar XML, creëert het een extra element met de naam BOGU;
  • BOGU minder kwaad dan structuur vanwege het feit dat de envelop compacter is (er worden geen extra namespaces toegevoegd in de XML-header van de envelop);
  • helaas valideert de SoapServer-klasse de envelopgegevens niet automatisch met ons XML-schema (misschien doen andere servers dat niet).
  • zelfstudie

Dag iedereen!
Toevallig ben ik de laatste tijd begonnen met het ontwikkelen van webservices. Maar vandaag gaat het onderwerp niet over mij, maar over hoe we onze XML Web Service kunnen schrijven op basis van het SOAP 1.2-protocol.

Ik hoop dat je na het lezen van het onderwerp in staat zult zijn om zelfstandig:

  • schrijf uw eigen server-side implementatie van een webapplicatie;
  • schrijf uw eigen client-side implementatie van een webapplicatie;
  • schrijf uw eigen webservicebeschrijving (WSDL);
  • door de client-arrays van hetzelfde type gegevens naar de server verzenden.
Zoals je misschien al geraden had, wordt alle magie gedaan met behulp van PHP en de ingebouwde SoapClient- en SoapServer-klassen. De service voor het verzenden van sms zal als een konijn werken.

1 Probleemstelling

1.1 Grenzen

In het begin stel ik voor om het resultaat te behandelen dat we aan het einde van het onderwerp zullen bereiken. Zoals hierboven aangekondigd, zullen we een dienst schrijven voor het verzenden van sms-berichten, of beter gezegd, we zullen berichten van verschillende bronnen ontvangen via het SOAP-protocol. Daarna zullen we bekijken in welke vorm ze naar de server komen. Het proces van het in de wachtrij plaatsen van berichten voor verdere verzending naar de provider valt helaas om vele redenen buiten het bestek van dit bericht.

1.2 Welke gegevens wijzigen wij?

Mooi, we hebben de grenzen bepaald! De volgende stap die moet worden gedaan, is om te beslissen welke gegevens we tussen de server en de client gaan uitwisselen. Over dit onderwerp stel ik voor om lange tijd niet wijs te zijn en onmiddellijk de belangrijkste vragen voor mezelf te beantwoorden:
  • Wat is de minimale hoeveelheid gegevens die naar de server moet worden verzonden om een ​​sms-bericht naar een abonnee te sturen?
  • Wat is de minimale hoeveelheid gegevens die vanaf de server moet worden verzonden om aan de behoeften van de klant te voldoen?
Iets zegt me dat hiervoor het volgende moet worden verzonden:
  • ook mobiel telefoonnummer
  • SMS-tekst.
In principe zijn deze twee kenmerken voldoende om te verzenden, maar ik stel me meteen een geval voor als er een sms met een verjaardagsgroet bij je komt om 3 uur 's nachts, of 4 uur! Op dit moment zal ik iedereen heel dankbaar zijn dat ze mij niet vergeten zijn! Daarom sturen we ook naar de server en
  • de datum van verzending van het sms-bericht.
Het volgende dat ik naar de server wil sturen is
  • Het type bericht.
Deze parameter is optioneel, maar het kan voor ons erg handig zijn als we de baas snel moeten vertellen hoeveel van onze klanten we "gelukkig hebben gemaakt" met ons nieuws, en ook wat mooie statistieken over deze score moeten trekken.

En toch ben ik iets vergeten! Als we wat meer nadenken, dan is het vermeldenswaard dat een klant zowel één sms-bericht als een aantal tegelijk naar de server kan sturen. Met andere woorden, één datapakket kan één tot oneindig veel berichten bevatten.

Als gevolg hiervan krijgen we dat we voor het verzenden van een sms-bericht de volgende gegevens nodig hebben:

  • Telefoon nummer,
  • sms-tekst,
  • tijdstip van het verzenden van een SMS-bericht naar een abonnee,
  • soort bericht.

We hebben de eerste vraag beantwoord, nu is het noodzakelijk om de tweede vraag te beantwoorden. En misschien zal ik mezelf een beetje hacken. Daarom sturen we vanaf de server alleen booleaanse gegevens, waarvan de waarde de volgende betekenis heeft:

  • TRUE - het pakket heeft de server met succes bereikt, is geverifieerd en is in de wachtrij geplaatst om naar de sms-provider te worden verzonden
  • ONWAAR - in alle andere gevallen

Hiermee is de beschrijving van de probleemstelling afgerond! En tot slot, laten we naar het meest interessante gaan - laten we uitzoeken wat een bizar beest deze SOAP is!

2 Waar is SOAP mee bedoeld?

In het algemeen was ik aanvankelijk niet van plan iets te schrijven over wat SOAP is en wilde ik me beperken tot links naar de w3.org-site met de nodige specificaties, evenals links naar Wikipedia. Maar helemaal aan het einde besloot ik een korte referentie over dit protocol te schrijven.

En ik zal mijn verhaal beginnen met het feit dat dit protocol voor gegevensuitwisseling behoort tot een subset van protocollen die gebaseerd zijn op het zogenaamde RPC-paradigma (Remote Procedure Call), waarvan het tegenovergestelde REST (Representational State Transfer) is. U kunt hier meer over lezen op Wikipedia, links naar artikelen staan ​​helemaal aan het einde van het onderwerp. Uit deze artikelen moeten we het volgende begrijpen: “De RPC-benadering stelt u in staat een kleine hoeveelheid netwerkbronnen te gebruiken met een groot aantal methoden en een complex protocol. Bij een REST-aanpak is het aantal methoden en de complexiteit van het protocol strikt beperkt, wat kan leiden tot een groot aantal individuele resources." Dat wil zeggen, voor ons betekent dit dat in het geval van de RPC-aanpak op de site er altijd één invoer (link) naar de service zal zijn en welke procedure moet worden aangeroepen om de binnenkomende gegevens te verwerken die we samen met de gegevens overdragen, terwijl met de REST-aanpak op onze site de site veel invoer (links) heeft, die elk alleen bepaalde gegevens accepteert en verwerkt. Als iemand die leest het verschil in deze benaderingen nog gemakkelijker weet uit te leggen, schrijf dan in de reacties!

Het volgende dat we over SOAP moeten weten, is dat dit protocol dezelfde XML als transport gebruikt, wat aan de ene kant erg goed is, omdat onmiddellijk in ons arsenaal komt alle kracht van de stapel technologieën op basis van deze opmaaktaal in ons arsenaal, namelijk XML-Schema - een taal voor het beschrijven van de structuur van een XML-document (dankzij Wikipedia!), waarmee automatische validatie van gegevens mogelijk is die van clients naar de server komen.

En dus weten we nu dat SOAP een protocol is dat wordt gebruikt om procedureaanroepen op afstand te implementeren en dat XML als transportmiddel gebruikt! Als je het artikel op Wikipedia leest, kun je van daaruit ook leren dat het bovenop elk applicatielaagprotocol kan worden gebruikt, en niet alleen in combinatie met HTTP (helaas zullen we in dit onderwerp alleen SOAP over HTTP beschouwen). En weet je wat ik het leukste vind aan dit alles? Als er geen gissingen zijn, dan zal ik je een hint geven - SOAP! ... Hoe dan ook, er waren geen gissingen? ... Je hebt het artikel op Wikipedia zeker gelezen? ... Over het algemeen zal ik je niet verder martelen . Daarom ga ik meteen naar het antwoord: “SOAP (van het Engelse Simple Object Access Protocol - simple protocol toegang tot objecten; tot specificatie 1.2) ". De meest opmerkelijke dingen aan deze regel zijn cursief gedrukt! Ik weet niet welke conclusies je uit dit alles hebt getrokken, maar ik zie het volgende - aangezien dit protocol niet "eenvoudig" kan worden genoemd (en het hier blijkbaar zelfs in w3) mee eens is, sinds versie 1.2 wordt het op de een of andere manier niet meer ontcijferd ! En het werd bekend als SOAP, gewoon SOAP-periode.

Wel, oké, neem me niet kwalijk, het slipte een beetje opzij. Zoals ik eerder schreef, wordt XML gebruikt als transport, en de pakketten die tussen de client en de server liggen, worden SOAP-enveloppen genoemd. Als we kijken naar de algemene structuur van de envelop, dan zal het je heel bekend voorkomen, omdat lijkt op de structuur van een HTML-pagina. Het heeft een hoofdgedeelte - Omhullen die secties bevat koptekst en Lichaam of Schuld... V Lichaam gegevens worden overgedragen en het is een verplicht onderdeel van de envelop, terwijl koptekst is optioneel. V koptekst autorisatie kan worden verzonden, of andere gegevens die niet direct verband houden met de invoergegevens van de webserviceprocedures. Wat betreft Schuld er valt niets bijzonders te vertellen, behalve dat het bij eventuele fouten van de server naar de client komt.

Dit besluit mijn overzichtsverhaal over het SOAP-protocol (we zullen de enveloppen zelf en hun structuur in meer detail bekijken wanneer onze client en server eindelijk leren hoe ze elkaar kunnen gebruiken) en een nieuwe begint - over de SOAP-compagnon genaamd WSDL(Webservices Beschrijving Taal). Ja, ja, dit is precies wat de meesten van ons afschrikken van de poging om onze API op dit protocol te gebruiken en te implementeren. Hierdoor vinden we met JSON meestal het wiel opnieuw uit voor transport. Dus wat is WSDL? WSDL is een taal voor het beschrijven en openen van webservices, gebaseerd op de XML (c) Wikipedia-taal. Als je vanuit deze definitie niet de hele heilige betekenis van deze technologie begrijpt, dan zal ik proberen het in mijn eigen woorden te beschrijven!

WSDL is zo ontworpen dat onze klanten normaal met de server kunnen communiceren. Hiervoor wordt in het bestand met de extensie "* .wsdl" de volgende informatie beschreven:

  • Welke naamruimten werden gebruikt,
  • Welke dataschema's werden gebruikt,
  • Welke soorten berichten verwacht de webservice van klanten,
  • Welke gegevens horen bij welke procedures van de webservice,
  • Welke procedures bevat de webservice,
  • Hoe de klant de webserviceprocedures moet noemen,
  • Naar welk adres moeten de oproepen van de klant worden gestuurd.
Zoals u kunt zien, is dit bestand de volledige webservice. Door het adres van het WSDL-bestand in de client op te geven, weten we alles over elke webservice! Hierdoor hoeven we helemaal niets te weten over waar de webservice zelf zich bevindt. U hoeft alleen de locatie van het WSDL-bestand te weten! Binnenkort zullen we leren dat SOAP niet zo verschrikkelijk is als het is geschilderd (c) Russische spreekwoorden.

3 Inleiding tot XML-schema

Nu weten we veel over wat SOAP is, wat erin zit, en hebben we een overzicht van de technology stack eromheen. Aangezien SOAP in de eerste plaats een manier van interactie is tussen een client en een server, en XML-opmaaktaal wordt gebruikt als transportmiddel, zullen we in deze sectie een beetje begrijpen hoe gegevens automatisch worden gevalideerd met behulp van XML-schema's.

De hoofdtaak van het schema is het beschrijven van de structuur van de gegevens die we gaan verwerken. Alle gegevens in XML-schema's zijn onderverdeeld in: eenvoudig(scalair) en complex(structuren) typen. Eenvoudige typen omvatten typen zoals:

  • lijn,
  • nummer,
  • booleaanse waarde,
  • datum.
Iets heel eenvoudigs waar geen extensies in zitten. Hun tegenpool is complexe complexe typen. Het eenvoudigste voorbeeld van een complex type dat iedereen kan bedenken, zijn objecten. Bijvoorbeeld een boek. Het boek bestaat uit eigenschappen: auteur, titel, prijs, ISBN-nummer enzovoort. En deze eigenschappen kunnen op hun beurt zowel eenvoudige als complexe zijn. En de taak van het XML-schema is om het te beschrijven.

Ik stel voor dat je niet ver gaat en een XML-schema schrijft voor ons sms-bericht! Hieronder vindt u de xml-beschrijving van het sms-bericht:

71239876543 Test bericht 2013-07-20T12: 00: 00 12
Ons complexe typeschema ziet er als volgt uit:


Dit item luidt als volgt: we hebben een variabele " bericht"Type" Bericht"En er is een complex type genaamd" Bericht", die bestaat uit een opeenvolgende reeks elementen" telefoon"Type snaar, « tekst"Type snaar, « datum"Type datum Tijd, « type"Type decimale... Deze typen zijn eenvoudig en zijn al gedefinieerd in de schemabeschrijving. Gefeliciteerd! We hebben zojuist ons eerste XML-schema geschreven!

Ik denk dat de betekenis van de elementen " element" en " complexType"Voor jou is alles min of meer duidelijk geworden, dus daar gaan we ons niet meer op focussen en gaan direct over op het componisten element" volgorde". Wanneer we het element componist " volgorde»We informeren dat de elementen die erin zijn opgenomen altijd in de volgorde moeten worden geplaatst die in het diagram is aangegeven, en dat ze ook allemaal vereist zijn. Maar wanhoop niet! Er zijn nog twee componistenelementen in XML-schema's: “ keuze" en " alle". componist " keuze"Informeert dat er een van de elementen moet zijn die erin worden vermeld, en de componist" alle"- elke combinatie van de vermelde elementen.

Zoals u zich herinnert, hebben we in het eerste deel van het onderwerp afgesproken dat één tot oneindig sms-berichten in een pakket kunnen worden verzonden. Daarom stel ik voor om te begrijpen hoe dergelijke gegevens in het XML-schema worden gedeclareerd. De algemene structuur van het pakket kan er als volgt uitzien:

71239876543 Testbericht 1 2013-07-20T12: 00: 00 12 71239876543 Testbericht N 2013-07-20T12: 00: 00 12
Het schema voor zo'n complex type ziet er als volgt uit:


Het eerste blok bevat de bekende declaratie van het complexe type “ Bericht". Als je het hebt gemerkt, dan is in elk eenvoudig type opgenomen in " Bericht", Er zijn nieuwe kwalificatiekenmerken toegevoegd" minOccurs" en " maxOccurs". Zoals je uit de naam zou kunnen raden, is de eerste ( minOccurs) geeft aan dat deze reeks ten minste één element van het type “ telefoon», « tekst», « datum" en " type", Terwijl de volgende ( maxOccurs) het attribuut verklaart ons dat er maximaal één van dergelijke elementen in onze reeks is. Als gevolg hiervan krijgen we, wanneer we onze schema's voor gegevens schrijven, de ruimste keuze in hun aanpassing!

Het tweede blok van het diagram verklaart het element " berichtenlijst"Type" Berichtenlijst". Het is duidelijk dat " Berichtenlijst"Is een complex type dat ten minste één element bevat" bericht", Maar het maximale aantal van dergelijke elementen is niet beperkt!

4 Uw eigen WSDL schrijven

Weet u nog dat WSDL onze webservice is? Ik hoop dat je het je herinnert! Terwijl we het schrijven, zal onze kleine webservice erop drijven. Daarom stel ik voor om niet vals te spelen.

Over het algemeen moeten we, om alles correct te laten werken, een WSDL-bestand met het juiste MIME-type naar de klant overbrengen. Om dit te doen, moet u uw webserver dienovereenkomstig configureren, namelijk het MIME-type voor bestanden met de extensie "* .wsdl" op de volgende regel instellen:

Applicatie / wsdl + xml
Maar in de praktijk stuur ik meestal de HTTP-header " tekst / xml»:

Header ("Inhoudstype: tekst / xml; charset = utf-8");
en alles werkte prima!

Ik wil je meteen waarschuwen dat onze eenvoudige webservice een nogal indrukwekkende beschrijving zal hebben, dus schrik niet, want de meeste tekst is verplicht en eenmaal geschreven, kun je constant van de ene webservice naar de andere kopiëren!

Aangezien WSDL XML is, moet u er in de allereerste regel direct over schrijven. Het root-element van het bestand moet altijd de naam " definities»:


Doorgaans bestaat WSDL uit 4-5 hoofdblokken. Het allereerste blok is de definitie van de webservice, of met andere woorden, het toegangspunt.


Er staat hier dat we een dienst hebben genaamd - " SMS-service". Kortom, u kunt alle namen in het WSDL-bestand wijzigen in wat u maar wilt. ze spelen absoluut geen rol.

Daarna kondigen we aan dat in onze webservice “ SMS-service"Er is een ingangspunt ("poort") genaamd" Sms-servicepoort". Het is naar dit ingangspunt dat alle verzoeken van clients naar de server worden verzonden. En we geven aan in het element " adres»Een link naar het handlerbestand dat verzoeken accepteert.

Nadat we een webservice hebben gedefinieerd en er een ingangspunt voor hebben gespecificeerd, moeten we de ondersteunde procedures eraan binden:


Hiervoor wordt vermeld welke bewerkingen en in welke vorm deze zullen worden aangeroepen. Die. voor de haven " Sms-servicepoort"Een binding wordt gedefinieerd onder de naam" SMSServiceBinding", die het type oproep heeft" rpc"En HTTP wordt gebruikt als het transmissieprotocol (transport). We hebben hier dus aangegeven dat we een RPC-aanroep zullen doen via HTTP. Daarna beschrijven we welke procedures ( operatie) worden ondersteund in de webservice. We ondersteunen slechts één procedure - " verstuur sms". Via deze procedure worden onze prachtige berichten naar de server gestuurd! Nadat de procedure is aangekondigd, moet worden aangegeven in welke vorm de gegevens worden verzonden. In dit geval wordt aangegeven dat standaard SOAP-enveloppen worden gebruikt.

Daarna moeten we de procedure aan berichten binden:


Om dit te doen, specificeren we dat onze "binding" van het type is " SmsServicePoortType"En in het element" poorttype»Met de naam van hetzelfde type geven we de binding van procedures aan berichten aan. En dus zal het inkomende bericht (van client naar server) de naam " stuurSmsVerzoek", En uitgaand (van server naar client)" stuurSmsReactie". Zoals alle namen in WSDL zijn de namen van inkomende en uitgaande berichten willekeurig.

Nu moeten we de berichten zelf beschrijven, d.w.z. inkomend en uitgaand:


Om dit te doen, voegen we de elementen " bericht"Met de namen" stuurSmsVerzoek" en " stuurSmsReactie" respectievelijk. Daarin geven we aan dat er een envelop naar de ingang moet komen, waarvan de structuur overeenkomt met het gegevenstype " Verzoek". Daarna retourneert de server een envelop met het gegevenstype - " Antwoord».

Nu moeten we het kleinste ding doen - een beschrijving van deze typen toevoegen aan ons WSDL-bestand! En hoe denkt u dat de WSDL inkomende en uitgaande gegevens beschrijft? Ik denk dat je alles al lang begreep en dat tegen jezelf zei met behulp van XML-schema's! En je zult helemaal gelijk hebben!


Je mag ons feliciteren! Onze eerste WSDL is geschreven! En we zijn een stap dichter bij het bereiken van dit doel.
Vervolgens zullen we kijken naar wat PHP ons biedt om onze eigen gedistribueerde applicaties te ontwikkelen.

5 Onze eerste SOAP-server

Eerder schreef ik dat we de ingebouwde SoapServer-klasse zullen gebruiken om een ​​SOAP-server in PHP te maken. Om ervoor te zorgen dat alle verdere acties op dezelfde manier plaatsvinden als de mijne, moet je je PHP een beetje aanpassen. Meer specifiek moet u ervoor zorgen dat de extensie "php-soap" is geïnstalleerd. Hoe je het op je webserver zet, lees je best op de officiële PHP-site (zie de lijst met referenties).

Nadat alles is geïnstalleerd en geconfigureerd, moeten we een bestand maken in de hoofdmap van uw hosting " smsservice.php"Met de volgende inhoud:

setClass ("SoapSmsGateWay"); // Start de server $ server-> handle ();
Ik hoop dat het niet nodig is om uit te leggen wat er boven de lijn staat met de functie "ini_set". Omdat daar wordt bepaald welke HTTP-headers we van de server naar de client gaan sturen en wordt de omgeving geconfigureerd. In de regel met "ini_set" schakelen we caching van het WSDL-bestand uit, zodat onze wijzigingen daarin onmiddellijk van kracht worden op de client.

Nu komen we bij de server! Zoals je kunt zien, is de hele SOAP-server slechts drie regels lang! In de eerste regel maken we een nieuwe instantie van het SoapServer-object en geven deze het adres van onze WSDL-webservicebeschrijving aan de constructor door. Nu weten we dat het zich in de root van de hosting zal bevinden in een bestand met de voor zichzelf sprekende naam " smsservice.wsdl.php". In de tweede regel vertellen we de SOAP-server welke klasse moet worden opgehaald om de van de klant ontvangen envelop te verwerken en de envelop met het antwoord terug te sturen. Zoals je misschien al geraden had, is het in deze klasse dat onze enige methode zal worden beschreven. verstuur sms... Op de derde regel starten we de server! Dat is alles, onze server is klaar! Waarmee ik ons ​​allemaal feliciteer!

Nu moeten we een WSDL-bestand maken. Om dit te doen, kunt u ofwel eenvoudig de inhoud van de vorige sectie kopiëren, of de vrijheid nemen en het een beetje "template":

"; ?> / "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/ "> /"> /smsservice.php "/>
In dit stadium zou de resulterende server volledig bij ons moeten passen, omdat: we kunnen enveloppen die binnenkomen registreren en vervolgens rustig de binnenkomende gegevens analyseren. Om iets op de server te kunnen ontvangen, hebben we een client nodig. Dus laten we het doen!

6 SOAP-client onderweg

Allereerst moeten we een bestand maken waarin we de klant zullen schrijven. Zoals gewoonlijk zullen we het in de root van de host maken en het een naam geven " client.php", En binnenin schrijven we het volgende:

messageList = nieuwe MessageList (); $ req-> messageList-> bericht = nieuw bericht (); $ req-> messageList-> message-> phone = "79871234567"; $ req-> messageList-> message-> text = "Testbericht 1"; $ req-> messageList-> message-> date = "2013-07-21T15: 00: 00.26"; $ req-> messageList-> bericht-> type = 15; $ client = nieuwe SoapClient ("http: // ($ _SERVER [" HTTP_HOST "]) / smsservice.wsdl.php, array (" soap_version "=> SOAP_1_2)); var_dump ($ client-> sendSms ($ req));
Laten we onze objecten beschrijven. Toen we de WSDL schreven, beschreef het drie entiteiten voor de envelop die de server binnenkwam: Verzoek, Berichtenlijst en Bericht... dienovereenkomstig klassen Verzoek, Berichtenlijst en Bericht zijn reflecties van deze entiteiten in ons PHP-script.

Nadat we de objecten hebben gedefinieerd, moeten we een object maken ( $ vereist), die we naar de server sturen. Daarna zijn er twee meest gekoesterde regels voor ons! Onze SOAP-klant! Geloof het of niet, maar dit is genoeg om berichten van de klant op onze server te laten stromen, en ook om onze server deze met succes te laten ontvangen en verwerken! In de eerste maken we een instantie van de SoapClient-klasse en geven het adres van de locatie van het WSDL-bestand door aan de constructor, en geven expliciet in de parameters aan dat we zullen werken met SOAP versie 1.2. Op de volgende regel noemen we de methode verstuur sms object $ klant en het resultaat onmiddellijk in de browser weergeven.
Laten we het uitvoeren en zien wat we uiteindelijk hebben!

Het volgende object is naar mij teruggestuurd van de server:

Object (stdClass) public "status" => boolean true
En dit is geweldig, want nu weten we zeker dat onze server werkt en niet alleen werkt, maar ook enkele waarden aan de klant kan retourneren!

Laten we nu eens kijken naar het logboek dat we voorzichtig aan de serverkant bewaren! In het eerste deel ervan zien we de onbewerkte gegevens die naar de server zijn gekomen:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15
Dit is de envelop. Nu weet je hoe het eruit ziet! Maar het zal voor ons nauwelijks interessant zijn om het constant te bewonderen, dus laten we het object uit het logbestand deserialiseren en kijken of alles goed met ons is:

Object (stdClass) public "messageList" => object (stdClass) public "message" => object (stdClass) public "phone" => string "79871234567" (lengte = 11) public "text" => string "Testbericht 1 "(lengte = 37) openbaar" datum "=> string" 2013-07-21T15: 00: 00.26 "(lengte = 22) openbaar" type "=> string" 15 "(lengte = 2)
Zoals je kunt zien, is het object correct gedeserialiseerd, waarmee ik ons ​​allemaal wil feliciteren! Vervolgens wacht ons iets interessanters! Namelijk - we sturen door de client niet één sms-bericht naar de server, maar een hele reeks (meer precies, drie)!

7 Complexe objecten verzenden

Laten we eens nadenken over hoe we een hele reeks berichten in één pakket naar de server kunnen sturen? Waarschijnlijk is de gemakkelijkste manier om de array in het messageList-element te ordenen! Laten we dit doen:

// maak een object om naar de server te sturen $ req = new Request (); $ req-> messageList = nieuwe MessageList (); $ msg1 = nieuw bericht (); $ msg1-> telefoon = "79871234567"; $ msg1-> text = "Testbericht 1"; $ msg1-> datum = "2013-07-21T15: 00: 00.26"; $ msg1-> type = 15; $ msg2 = nieuw bericht (); $ msg2-> telefoon = "79871234567"; $ msg2-> text = "Testbericht 2"; $ msg2-> datum = "2014-08-22T16: 01: 10"; $ msg2-> type = 16; $ msg3 = nieuw bericht (); $ msg3-> telefoon = "79871234567"; $ msg3-> text = "Testbericht 3"; $ msg3-> datum = "2014-08-22T16: 01: 10"; $ msg3-> type = 17; $ req-> messageList-> bericht = $ msg1; $ req-> messageList-> bericht = $ msg2; $ req-> messageList-> bericht = $ msg3;
Uit onze logboeken blijkt dat het volgende pakket afkomstig is van een klant:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15 79871234567 Testbericht 2 2014-08-22T16: 01: 10 16 79871234567 Testbericht 3 2014-08-22T16: 01: 10 17
Wat een onzin, zegt u? En in zekere zin heb je gelijk, want we hebben zojuist vernomen welk object de client heeft verlaten, en toen kwam het in absoluut dezelfde vorm naar onze server in de vorm van een envelop. Sms-berichten waren echter niet geserialiseerd naar XML zoals we ze nodig hadden - ze moesten in elementen worden verpakt bericht, niet in structuur... Laten we nu eens kijken in welke vorm zo'n object tot de methode komt verstuur sms:

Object (stdClass) public "messageList" => object (stdClass) public "message" => object (stdClass) public "Struct" => array (grootte = 3) 0 => object (stdClass) public "phone" => string "79871234567" (lengte = 11) openbaar "tekst" => string "Testbericht 1" (lengte = 37) openbaar "datum" => string "2013-07-21T15: 00: 00.26" (lengte = 22) openbaar " typ "=> string" 15 "(length = 2) 1 => object (stdClass) public" phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 2 "(length = 37) public "date" => string "2014-08-22T16: 01: 10" (lengte = 19) public "type" => string "16" (lengte = 2) 2 => object (stdClass) public "phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 3 "(length = 37) public" date "=> string" 2014-08-22T16: 01: 10 "(length = 19) openbaar "type" => string "17" (lengte = 2)
Wat levert deze kennis ons op? Alleen dat het pad dat we hebben gekozen niet correct is en we geen antwoord hebben gekregen op de vraag - "Hoe kunnen we de juiste datastructuur op de server krijgen?" Maar ik stel voor om niet te wanhopen en te proberen onze array naar het type te casten een voorwerp:

$ req-> messageList-> bericht = (object) $ req-> messageList-> bericht;
In dit geval ontvangen we nog een envelop:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15 79871234567 Testbericht 2 2014-08-22T16: 01: 10 16 79871234567 Testbericht 3 2014-08-22T16: 01: 10 17
Kwam in methode verstuur sms het object heeft de volgende structuur:

Object (stdClass) public "messageList" => object (stdClass) public "message" => object (stdClass) public "BOGUS" => array (grootte = 3) 0 => object (stdClass) public "phone" => string "79871234567" (lengte = 11) openbaar "tekst" => string "Testbericht 1" (lengte = 37) openbaar "datum" => string "2013-07-21T15: 00: 00.26" (lengte = 22) openbaar " typ "=> string" 15 "(length = 2) 1 => object (stdClass) public" phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 2 "(length = 37) public "date" => string "2014-08-22T16: 01: 10" (lengte = 19) public "type" => string "16" (lengte = 2) 2 => object (stdClass) public "phone "=> string" 79871234567 "(length = 11) public" text "=> string" Testbericht 3 "(length = 37) public" date "=> string" 2014-08-22T16: 01: 10 "(length = 19) openbaar "type" => string "17" (lengte = 2)
Wat mij betreft, dan "van een verandering in de plaatsen van de voorwaarden - de som verandert niet" (c). Wat BOGU, wat structuur- het doel is door ons nog niet bereikt! En om dit te bereiken, moeten we ervoor zorgen dat in plaats van deze onbegrijpelijke namen, onze native wordt weergegeven bericht... Maar hoe dit te bereiken, weet de auteur nog niet. Daarom is het enige wat we kunnen doen de extra container weg te doen. Met andere woorden, we zullen het nu zo maken dat in plaats van: bericht werd BOGU! Laten we hiervoor het object als volgt wijzigen:

// maak een object om naar de server te sturen $ req = new Request (); $ msg1 = nieuw bericht (); $ msg1-> telefoon = "79871234567"; $ msg1-> text = "Testbericht 1"; $ msg1-> datum = "2013-07-21T15: 00: 00.26"; $ msg1-> type = 15; $ msg2 = nieuw bericht (); $ msg2-> telefoon = "79871234567"; $ msg2-> text = "Testbericht 2"; $ msg2-> datum = "2014-08-22T16: 01: 10"; $ msg2-> type = 16; $ msg3 = nieuw bericht (); $ msg3-> telefoon = "79871234567"; $ msg3-> text = "Testbericht 3"; $ msg3-> datum = "2014-08-22T16: 01: 10"; $ msg3-> type = 17; $ req-> messageList = $ msg1; $ req-> messageList = $ msg2; $ req-> messageList = $ msg3; $ req-> messageList = (object) $ req-> messageList;
Wat als we geluk hebben en de juiste naam uit het diagram wordt getrokken? Laten we hiervoor de ontvangen envelop bekijken:

79871234567 Testbericht 1 2013-07-21T15: 00: 00.26 15 79871234567 Testbericht 2 2014-08-22T16: 01: 10 16 79871234567 Testbericht 3 2014-08-22T16: 01: 10 17
Ja, het wonder is niet gebeurd! BOGU- we zullen niet winnen! Kom binnen verstuur sms het object ziet er in dit geval als volgt uit:

Object (stdClass) public "messageList" => object (stdClass) public "BOGUS" => array (size = 3) 0 => object (stdClass) public "phone" => string "79871234567" (length = 11) public " tekst "=> string" Testbericht 1 "(lengte = 37) openbaar" datum "=> tekenreeks" 2013-07-21T15: 00: 00.26 "(lengte = 22) openbaar" type "=> tekenreeks" 15 "(lengte = 2) 1 => object (stdClass) public "phone" => string "79871234567" (lengte = 11) public "text" => string "Testbericht 2" (lengte = 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 "Testbericht 3" (lengte = 37) public "date" => string "2014-08-22T16: 01: 10" (lengte = 19) public "type" => string " 17 "(lengte = 2)
Zoals ze zeggen - "Bijna"! Op deze (een beetje droevige) noot stel ik voor om geleidelijk af te ronden en enkele conclusies voor mezelf te trekken.

8 Conclusie

Eindelijk zijn we hier! Laten we definiëren wat u nu kunt doen:
  • u kunt het vereiste WSDL-bestand voor uw webservice schrijven;
  • je kunt zonder problemen je eigen client schrijven die met de server kan communiceren via het SOAP-protocol;
  • je kunt je eigen server schrijven die via SOAP met de buitenwereld communiceert;
  • je kunt arrays van objecten van hetzelfde type naar de server sturen vanaf je client (met enkele beperkingen).
We hebben ook enkele ontdekkingen voor onszelf gedaan in de loop van ons kleine onderzoek:
  • de native SoapClient-klasse kan datastructuren van hetzelfde type in XML niet correct serialiseren;
  • bij het serialiseren van een array naar XML, wordt een extra element gemaakt met de naam structuur;
  • bij het serialiseren van een object naar XML, creëert het een extra element met de naam BOGU;
  • BOGU minder kwaad dan structuur vanwege het feit dat de envelop compacter is (er worden geen extra namespaces toegevoegd in de XML-header van de envelop);
  • helaas valideert de SoapServer-klasse de envelopgegevens niet automatisch met ons XML-schema (misschien doen andere servers dat niet).

ZEEP is een op tekst gebaseerd protocol dat XML gebruikt om gestructureerde berichten uit te wisselen in een gedistribueerde computeromgeving. SOAP was oorspronkelijk vooral bedoeld om Remote Procedure Call (RPC) te implementeren en de naam was een acroniem: Simple Object Access Protocol. Het protocol wordt nu gebruikt om willekeurige XML-berichten uit te wisselen, niet alleen procedureaanroepen. De officiële specificatie van de nieuwste versie 1.2 van het protocol ontcijfert de naam SOAP op geen enkele manier. SOAP is een uitbreiding van het XML-RPC-protocol. SOAP kan worden gebruikt met elk protocol op toepassingsniveau: SMTP, FTP, HTTP, enz. De interactie met elk van deze protocollen heeft echter zijn eigen kenmerken, die afzonderlijk moeten worden gedefinieerd. Meestal wordt SOAP gebruikt via HTTP. SOAP is een van de standaarden waarop webservicetechnologie is gebaseerd. De communicatie tussen webservices en hun klanten verloopt via XML-berichten. SOAP (Simple Object Access Protocol) is een berichtenprotocol voor het selecteren van webservices. We kunnen zeggen dat SOAP ideaal is voor Remote Procedure Call (RPC)-technologie, omdat het SOAP-bericht door de klant geleverde parameters of een door een service verzonden retourwaarde bevat.

Voordelen van het gebruik van SOAP:

· Flexibelere gegevenstypen.

Ondersteuning voor headers en extensies:

nadelen:

· Het gebruik van SOAP om berichten over te brengen verhoogt het volume en vertraagt ​​de verwerkingssnelheid. In systemen waar snelheid belangrijk is, is het gebruikelijker om XML-documenten rechtstreeks via HTTP te verzenden, waarbij verzoekparameters worden doorgegeven als normale HTTP-parameters.

· Hoewel SOAP een standaard is, genereren verschillende programma's vaak berichten in incompatibele formaten. Een verzoek dat door een AXIS-client wordt gegenereerd, wordt bijvoorbeeld niet begrepen door de WebLogic-server.

Basis protocol concepten: De partij die het SOAP-bericht verzendt, wordt de SOAP-afzender genoemd en de partij die het ontvangt, is de SOAP-ontvanger. Het pad dat een SOAP-bericht aflegt van de oorspronkelijke afzender naar de uiteindelijke ontvanger, wordt de berichtroute genoemd. De berichtenroute bevat een oorspronkelijke afzender, een uiteindelijke ontvanger en 0 of meer SOAP-tussenpersonen. Objecten die berichten verwerken volgens specifieke SOAP-regels worden SOAP-knooppunten genoemd. De elementaire informatie-eenheid die deelneemt aan de uitwisseling tussen SOAP-knooppunten wordt een SOAP-bericht genoemd - het is een XML-document met een SOAP-wrapper rond het root-element.