Het is tijd om het over SOAP te hebben. Nee, niet het schoonmaakproduct maar het Simple Object Access Protocol. SOAP is een veelvoorkomend protocol dat gebruikt wordt in web services en als we naar de huidige versie van WSO2 Enterprise Integrator kijken is het de essentiële technologie van dit product. SOAP is gebaseerd op XML en een SOAP-bericht bestaat uit een envelop met een header en een body. In deze blog gaan we kijken naar de webservice beschrijvingstaal (Eng.: web service description language, of kortweg WSDL) die vaak gebruikt wordt om de interface of service te beschrijven die SOAP gebruikt.
WSO2 Enterprise Integrator
SOAP is wat van zijn populariteit verloren aan JSON, maar het wordt nog steeds vaak gebruikt in tal van webservices. Als we naar het acronym van SOAP kijken, zien we het woord Simple. Dat is op een bepaalde manier misleidend. Omdat SOAP eigenlijk helemaal niet zo simpel is, vooral als we het vergelijken met JSON. Toch gebruiken we nog steeds SOAP-services en dat zullen we naar verwachting nog wel een tijd blijven doen. Laten we kijken naar de webservice beschrijvingstaal, WSDL, die vaak gebruikt wordt om de interface of service te beschrijven die door SOAP gebruikt worden. Let op, ik ga niet gedetailleerd in op het installeren van de WSO2 Enterprise Integrator of de Integration Studio of het creëren en hoe je een proxy inzetten door middel van een car-bestand op de WSO2 Enterprise Integrator. Daarvoor kun je een kijkje nemen naar de ingevoegde links.
WSDL
Een WSDL bepaalt de interface van een webservice. Een interface bestaat uit operaties en berichten. Deze berichten worden door middel van SOAP verstuurd. In feite beschrijft het de inhoud van de SOAP-body met betrekking tot de XML-elementen die gebruikt moeten worden. Het doel is om je te helpen een bericht te maken met de juiste structuur en inhoud. Als je een SOAP service gebruikt, kun je vaak een WSDL-bestand vinden om je te helpen het juiste bericht te creëren. Laten we eens kijken naar een WSDL-bestand dat bij de Enterprise Integrator inbegrepen is. Ik heb het over de echo service als onderdeel van de EI.
Dit is het overzicht van Deployed Services in de WSO2 Enterprise Integrator. Je kunt zien dat de echo service geen proxy of API is, maar een axis2 service, de web engine die gebruikt wordt in WSO2 Enterprise Integrator. We gaan de WSDL v1.1 definitie bekijken. Als we op de WSDL-link klikken, dan opent die in de Chrome browser (in Firefox laat het de inhoud niet zien). Dit is hoe het eruitziet (ingekorte versie voor het overzicht):
Zoals je kunt zien is er een element dat echoString heet. Dit bevat een element dat ‘in’ heet en een string-waarde verwacht. Als we de WSDL gebruiken om een SOAP project aan te maken in SoapUI is deze definitie gewijzigd, zoals te zien is in het onderstaande bericht:
Validatie
Er zijn verschillende manieren om te valideren of een bericht voldoet aan de bijbehorende WSDL. Als het bericht de Enterprise Integrator binnenkomt kun je kijken naar de individuele elementen in de SOAP body, bijvoorbeeld om te controleren of de XML tags correct zijn.
We gaan een simpele proxy maken die gaat controleren of er een correcte echoString tag in het bericht aanwezig is. We gebruiken de WSO2 Enterprise Integrator 6.5.0 en de WSO2 Integration Studio.
Ik heb een simpele pass-through proxy aangemaakt die naar het eindpunt van de echo service verwijst. Als het bericht de EI binnenkomt is de SOAP body beschikbaar in de proxy en ik kan dit met een Full Log mediator inzien. De broncode is eenvoudig, dus ik laat alleen de grafische weergave zien.
Ik ga deze proxy-aanroepen met SoapUI, waarbij ik de originele WSDL van de Echo service gebruik. Dit doe ik omdat ik WSDL niet op de proxy service heb gepubliceerd en anders zou ik een standaard WSDL te zien krijgen met een lege body. Deze verandering houdt in dat er twee parameters bijkomen die aangeven welke WSDL we willen willen gebruiken: die van de backend of een zelf gedefinieerde WSDL. Als we de service aanroepen met het bericht dat gegenereerd is door SoapUI, dan krijg ik zowel een reactie in SoapUI als een bericht op de console.
Ik laat een deel van het bericht op de console zien:
[2019-07-01 10:13:08,535] [EI-Core] INFO - LogMediator To: /services/WSDLCheck1,
WSAction: urn:echoString, SOAPAction: urn:echoString, MessageID: urn:uuid:e70f26c7-
19ec-433a-90d5-99fe42bc3328 correlation_id : 502325b8-ab98-4056-b8f1-939b86871d76,
Direction: request, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns_soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns_echo="http://echo.services.core.carbon.wso2.org"><soapenv:Body>
<echo:echoString>
<!--Optional:-->
<in>test</in>
</echo:echoString>
</soapenv:Body></soapenv:Envelope>
Zoals je kunt zien is er een volledig bericht binnengekomen. Dus laten we nu een filter aan de mediator toevoegen om te bepalen welke van twee situaties van toepassing is:
- Het bericht bevat een tag die echoString heet
- Het bericht bevat geen tag die echoString heet
De filter mediator is ideaal voor dit soort setups, omdat we maar twee mogelijke uitkomsten hebben. Om te controleren of de string beschikbaar is kunnen we Xpath gebruiken. Xpath is voor XML-berichten hetzelfde als SQL voor een database. Met andere woorden: het is een taal die ons toestaat informatie te halen uit, in dit geval, een bericht. Als je onbekend bent met Xpath (dat kan me voorstellen) zijn er online enkele hulpmiddelen die je kunnen helpen met je begrip van deze belangrijke taal die gebruikt wordt in de Enterprise Integrator en andere soorten berichten die op XML gebaseerd zijn.
Het hulpmiddel heet https://www.freeformatter.com/xpath-tester.html en je kunt er allerlei vormen van Xpath statements mee uitproberen, zodat je de vereiste informatie uit een XML-bericht kunt halen. Kopieer simpelweg het bericht van SoapUI naar de Xpath tester en test daar je Xpath-instructie. Vergeet echter niet dat namespaces opgenomen moeten worden als je Xpath gebruikt om waardes op te halen.
Dus, wat ik graag zou willen doen is op het bericht filteren en uitvinden of het bericht de WSDL volgt, voor minimaal een element. Ik doe dat door de inhoud van het echoString element te controleren. Als er een element is dat echoString heet, dan zal het de inhoud teruggeven (zelfs als het leeg is).
De expressie die ik gebruik om dit te bepalen is als volgt:
<property expression="descendant::echo:echoString/in" name="CHKWSDL1" xmlns_echo=http://echo.services.core.carbon.wso2.org />
Dus, wat doe ik hier? We hebben een property-mediator met een expressie die de waarde van het ‘in’-XML-element binnen het echoString-element ophaalt. Als het bericht dat naar de proxy wordt gestuurd bekijkt, kun je het ‘in’-element zien. Ik gebruik de zogenoemde ‘XPath axes’ om de echo:echoString ergens in de berichtbody te vinden en daarbinnen haal ik het ‘in’-element op.
Het echoString-element is gedefinieerd met een namespace die de ‘echo’ prefix heeft en we moeten die namespace in de Xpath-instructie specificeren om het element op te kunnen halen.
In begrijpelijke taal, zoek een <echoString> tag binnen de body en pak het subelement dat <in> heet en geef daar de waarde van terug.
Als de tag of tags waar ik naar zoek niet bestaan, krijg ik een leeg bericht terug en kan ik de foutafhandeling regelen. In dit geval zou ik een SOAP-fout willen aanmaken door de fault-mediator te gebruiken en de fout terug te sturen naar de client. In het geval dat ik geen leeg bericht terugkrijg, ook al is de waarde binnen de <in> tags leeg, stuur ik het gewoon naar de backend.
Grafisch ziet dat er zo uit:
<?xml version="1.0" encoding="UTF-8"?>
<proxy name="WSDLCheck1" startOnLoad="true" transports="http https"
xmlns=http://ws.apache.org/ns/synapse
xmlns:echo="http://echo.services.core.carbon.wso2.org">
<target>
<endpoint>
<address uri="http://localhost:8280/services/echo"/>
</endpoint>
<inSequence>
<log>
<property expression="descendant::echo:echoString/in" name="CHKWSDL1" />
</log>
<filter xpath="descendant::echo:echoString/in">
<then>
<log level="custom">
<property name="Outcome" value="Yes"/>
<property expression="descendant::echo:echoString/in" name="CHKWSDL1" />
</log>
</then>
<else>
<log level="custom">
<property name="Outcome" value="No"/>
<property expression="descendant::echo:echoString/in" name="CHKWSDL1" />
</log>
<makefault version="soap11">
<code value="soap11Env:Client" xmlns:soap11Env="http://schemas.xmlsoap.org/soap/envelope/"/>
<reason value="Message not according to WSDL, <echoString> is missing"/>
<detail>Sending incorrect message</detail>
</makefault>
<respond/>
</else>
</filter>
</inSequence>
<outSequence>
<send/>
</outSequence>
<faultSequence/>
</target>
</proxy>
In de bronweergave kunnen we duidelijk de individuele stappen onderscheiden. Met de eerste log mediator, die de waarde ophaalt, laten we eerst de waarde op het scherm zien. Daarna kijken we in de filter mediator of het element bestaat. Als het niet bestaat voeren we de ‘else’-tak van de filter mediator uit en loggen we een bericht op de console. We maken een SOAP-fout met de fault-mediator en geven het bericht terug met een respond-mediator. Als het element wel bestaat, dan loggen we het bericht op de console en sturen we het bericht impliciet door naar de backend echoservice.
Een simpele aanpak
Dit is een erg simpele aanpak om te controleren of een bericht het in de WSDL gespecificeerde element bevat. Hou in de gaten dat als je een eind- of openingstag mist, er een foutmelding op een lager niveau komt wanneer het bericht geparsed wordt. Hierdoor zal het de proxy niet bereiken. Een parsing error wordt door de WSO2 Enterprise Integrator automatisch naar de client teruggegeven.
In een volgende blog gaan we kijken naar een andere manier om een bericht te valideren om te zien of het de bijbehorende WSDL volgt. Dit gaan we doen door de validate mediator te gebruiken die beschikbaar is als deel van de WSO2 Enterprise Integrator set van mediators.