Novšia verzia
Článok má novšiu verziu na https://novotnyr.github.io/scrolls/od-wsdl-k-webovej-sluzbe-tvorime-vlastne-wsdl/
Úvod
Pri návrhu webových služieb nastáva sa môžeme vydať dvoma rozličnými cestami. Pri tom jednoduchšom postavíme webovú službu vo vhodnej technológii (JAX-WS, či PHP-WSDL Creator, a WSDL si necháme vygenerovať automaticky, čím dosiahneme tzv contract-last spôsob.
Opačný spôsob, contract-first spočíva v nadefinovaní WSDL, na základe ktorého si necháme vygenerovať jednak kostru pre server, a neskôr i klienta.
Postup od WSDL k triedam má mnohé výhody. Ota ‘tapik’ Buchta napr. uvádza:
- tvůrce se musí nejdříve zamyslet nad strukturou dat, což nebývá tak úplně zvykem
- to zabrání nutnosti neustále měnit rozhraní (a tím i WSDL) při změně datového modelu
- tvůrce uvažuje v intencích dokumentů a operací nad nimi, což mu umožní přehoupnout se přes onen RPC XORBA zlozvyk
- WSDL je čitelné a “hezké” a interoperabilní (pokud je navrženo jako document-wrapped (.NET nic jiného neumí))
- celý návrh systému je pak mnohem lépe udržovatelný, modularizovatelný, rozšiřitelný, znovupoužitelný, lépe odladitelný
Dokumentácia k Spring Web Services, čo je štandardná knižnica pre contract-first spôsob, podporuje tento prístup ďalšími významnými bodmi, ktoré môžu spôsobovať problémy:
- neprenositeľné dátové typy (za všetky spomeňme napr. mapy/slovníky) a cyklické objektové grafy
- so zmenou dát či modelu sa môže zmeniť WSDL, čo má vplyv na robustnosť
V článku si ukážeme, ako je možné vybudovať službu práve týmto zložitejším spôsobom. Ukážeme si, ako možno pochopiť jednotlivé stavebné bloky WSDL, ako ich možno vytvárať ručne, a necháme si tiež vygenerovať zdrojový kód pre JAX-WS server.
Predpokladané znalosti
- Veľmi vám uľahčí život, ak budete poznať zásady XML a menných priestorov, napr. z krátkeho tutoriálu.
- Oplatí sa tiež poznať XML schému a zásady jej tvorby.
Príklad a štruktúra dát
V našom príklade budeme chcieť vybudovať webovú službu pre rezerváciu lístkov v kine. Klient zašle požiadavku, v ktorej špecifikuje názov filmu, dátum jeho premietania a počet lístkov, ktoré si chce zarezervovať.
Požiadavka by mohla vyzerať nasledovne:
<movieReservationRequest>
<title>Krv, črevá a lietajúce hlavy</title>
<date>2008-12-24</date>
<numberOfTickets>4</numberOfTickets>
</movieReservationRequest>
Odpoveďou by mohol byť dokument obsahujúci číslo rezervácie a zoznam miest na sedenie, ktoré boli zarezervované:
<movieReservationResponse>
<id>2323241</id>
<reservedSeats>23</reservedSeats>
<reservedSeats>12</reservedSeats>
<reservedSeats>12</reservedSeats>
</movieReservationResponse>
Na základe týchto XML dokumentov si môžeme napísať XML schému, ktorá bude definovať štruktúru pre dáta na vstupe (požiadavky) a na výstupe (odpovede). Bez definície XML schémy nevieme špecifikovať formát vstupných a výstupných dokumentov vo WSDL.
Vzťah XML schéma-dokument je podobný vzťahu trieda-objekt z OOP. XML schéma (“trieda”) definuje predpis, teda štruktúru, hierarchiu a dátové typy jednotlivých elementov, a konkrétny XML dokument (“objekt”) predstavuje inštanciu tejto XML schémy.
XML schému možno navrhovať viacerými spôsobmi, ale pre nás je najvhodnejší štýl „matrioška” (russian doll). Pre každý z elementov priamo určíme jeho štruktúru.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:X-movies">
<xsd:element name="movieReservationRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="date" type="xsd:date" />
<xsd:element name="numberOfTickets" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="movieReservationResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:int" />
<xsd:element name="reservedSeats"
type="xsd:int"
minOccurs="1"
maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
V koreňovom elemente dokumentu môže byť buď element movieReservationRequest
(ak ide
o požiadavku) alebo movieReservationResponse
(ak pôjde o dokument z odpovede). Prvý z nich má tri podelementy:
title
(názov), date
(dátum) alebo numberOfTickets
(počet lístkov).
Druhý z nich je podobný: má id
(identifikátor rezervácie) a niekoľko
elementov reservedSeats
(zoznam miest).
Všetky deklarované elementy budú spadať do cieľového menného priestoru (target namespace)
urn:X-movies
.
Ukážkový dokument, ktorý spĺňa danú schému, vyzerá nasledovne:
<movieReservationRequest xmlns="urn:X-movies">
<title>Krv, črevá a lietajúce hlavy</title>
<date>2008-12-24</date>
<numberOfTickets>4</numberOfTickets>
</movieReservationRequest>
Dokumentu sme priradili implicitný menný priestor určený schémou
(urn:X-movies
).
Schému môžeme dokonca publikovať na konkrétnej webovej adrese, dostupnej cez webový prehliadač.
Ak by sa schéma ocitla na adrese http://movie.novotnyr.sk/schema.xsd
, môžeme mať dokument,
ktorý vieme priamo validovať oproti schéme.
<movieReservationRequest
xmlns="urn:X-movies"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:X-movies http://movie.novotnyr.sk/schema.xsd">
<title>Krv, črevá a lietajúce hlavy</title>
<date>2008-12-24</date>
<numberOfTickets>4</numberOfTickets>
</movieReservationRequest>
V tomto dokumente sme deklarovali menný priestor pre inštanciu schémy xsi
a samozrejme mapovanie menného priestoru na príslušnú schému: v tomto prípade elementy z menného priestoru urn:X-movies
možno validovať oproti schéme zverejnenej na adrese http://movie.novotnyr.sk/schema.xsd
.
WSDL súbor
Minimalistický WSDL súbor bez akýchkoľvek deklarácii vyzerá nasledovne:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="urn:X-movies"
targetNamespace="urn:X-movies"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
name="movie"
>
</wsdl:definitions>
Najdôležitejšie sú definície prefixov menných priestorov:
- prefix
wsdl
je používaný vo všetkých elementoch, ktorými deklarujeme základné stavebné prvky WSDL. V tomto prípade ho mapujeme na menný priestorhttp://schemas.xmlsoap.org/wsdl/
, ktorý zodpovedá norme WSDL 1.1. - prefix
tns
a atribúttargetNamespace
obsahujú (rovnaký) cieľový menný priestor pre elementy deklarované v rámci tohto WSDL súboru. Pre jednoduchosť volíme rovnaký cieľový menný priestor ako v prípade XML schémy - prefix
soap
sa vzťahuje na menný priestorhttp://schemas.xmlsoap.org/wsdl/soap/
Popri tom definujeme logický názov (name
) tohto WSDL súboru.
Schéma a dátové typy
V prvom kroku potrebujeme
zadefinovať štruktúru vstupných a výstupných dát, čo zabezpečíme sekciou
<wsdl:types>
. Máme dve možnosti: buď uviesť deklaráciu štruktúry priamo, zanorením elementov XML schémy, alebo sa vieme odkázať na externú XML schému prítomnú v samostatnom súbore.
Priamo uvedená schéma
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="urn:X-movies" targetNamespace="urn:X-movies" name="movie">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:X-movies">
<xsd:element name="movieReservationRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="date" type="xsd:date" />
<xsd:element name="numberOfTickets" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="movieReservationResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:int" />
<xsd:element name="reservedSeats" type="xsd:int" minOccurs="1" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
</wsdl:definitions>
Všimnime si, ako sme medzi elementy <wsdl:types>
…</wsdl:types>
bez zmeny vložili celú deklaráciu XML schémy, ktorú sme definovali v predošlej sekcii.
Importnutá schéma
Ak máme XML schému definovanú vo verejne dostupnom súbore prítomnom na adrese http://movie.novotnyr.sk/schema.xsd
, môžeme ju priamo importnúť do WSDL súboru.
<wsdl:types>
<xsd:schema>
<xsd:import schemaLocation="http://movie.novotnyr.sk/schema.xsd"
namespace="urn:X-movies" />
</xsd:schema>
</wsdl:types>
Všetky elementy zo schémy sa po importe ocitnú v mennom priestore urn:X-movies
, čo je zhodou okolností rovnaký menný priestor ako cieľový menný priestor WSDL schémy. (A v porovnaní s predošlým priamym uvedením schémy: je to rovnaká situácia, ako keď elementy XML schémy uvedieme do príslušného cieľového menného priestoru s použitím targetNamespace
na elemente <xml:schema>
.
Správy
Ďalej potrebujeme definovať abstraktné správy, ktoré budú putovať medzi
klientom a serverom. Je zjavné, že správy budú dve: požiadavka
movieReservationRequest
a odpoveď movieReservationResponse
. Každá
bude mať jednu časť part
tvorenú príslušným koreňovým elementom.
Požiadavka
Požiadavka bude pozostávať z koreňového elementu
movieReservationRequest
v mennom priestore s prefixom tns
(ten je
namapovaný na cieľový menný priestor http://movie.novotnyr.sk/ws/types
.
<wsdl:message name="movieReservationRequest">
<wsdl:part name="movieReservationRequestPart" element="tns:movieReservationRequest"/>
</wsdl:message>
Odpoveď
Odpoveď na požiadavku pozostáva z koreňového elementu
movieReservationResponse
, ktorý sa nachádza tiež v mennom priestore
s prefixom tns
.
<wsdl:message name="movieReservationResponse">
<wsdl:part name="movieReservationResponsePart" element="tns:movieReservationResponse"/>
</wsdl:message>
Typy pre port s operácie v ňom
Teraz definujeme typ pre port, ktorý bude logicky združovať niekoľko operácií. Môžeme sa naň dívať ako na analógiu Java interfejsu, kde operáciám zodpovedajú metódy.
Typ pre port predstavuje definíciu, ktorá uvádza, ktoré operácie sú na porte k dispozícii, a ako vyzerajú vstupné a výstupné dokumenty. Zatiaľ nie je nič povedané o konkrétnom protokole (HTTP, Jabber, a podobne), čiže inými slovami definujeme len syntax správ a sadu operácií nad nimi.
V ukážke definujeme typ pre port s názvom movieReservationPort
s jedinou
operáciou movieReservation
. V operácii definujeme odkaz na vstupnú
správu (tns:movieReservationRequest
) a tiež na výstupnú správu
(tns:movieReservationRequest
).
<wsdl:portType name="movieReservationPort">
<wsdl:operation name="movieReservation">
<wsdl:input message="tns:movieReservationRequest" />
<wsdl:output message="tns:movieReservationResponse" />
</wsdl:operation>
</wsdl:portType>
Binding
Až v tejto chvíli sa posunieme od abstraktnej syntaxe správ ku konkrétnemu tvaru odchádzajúcich a prichádzajúcich správ. Ten definujeme pomocou bindingu, ktorého úloha je:
- definovať transportný protokol: napr. HTTP, Mail, či Jabber
- štýl správ: konkrétny tvar volaní operácií.
Každý typ pre port môže definovať svoj vlastný binding, čo znamená, že môžeme definovať rozličné komunikačné protokoly pre rozličné operácie v porte. Konkrétny binding môže byť bližšie špecifikovaný pre každú operáciu zvlášť.
V príklade definujeme binding movieReservationBinding
, ktorý
asociujeme s typom portu tns:movieReservationPort
, ktorý sme definovali
pred chvíľou. Ďalej definujeme transportný protokol: bude ním SOAP nad
HTTP v štýle document.
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
Následne musíme pre každú operáciu definovanú v type portu určiť
konkrétny fyzický formát jednotlivých správ. Pre operáciu
movieReservation
budeme pre vstup i výstup používať správy v štýle
literal.
Jednotlivé štýly správ sú v tomto momente nepodstatné. Štýl, ktorý má najväčšiu podporu medzi klientmi, je
document/literal
. Podrobnosti možno nájsť v článku Which style of WSDL should I use?)
<wsdl:binding name="movieReservationBinding"
type="tns:movieReservationPort">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="movieReservation">
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
Služba
Ako poslednú náležitosť nastavíme službu service, ktorá definuje
konkrétnu adresu, na ktorej sú jednotlivé porty a operácie fyzicky dostupné.
Služba logicky zoskupuje viacero portov, pričom každý port predstavuje kombináciu typu pre port s
konkrétnym bindingom. V našom prípade definujeme službu
movieReservationService
obsahujúcu port movieReservationPort
, ktorý
sa vzťahuje k bindingu tns:movieReservationBinding
a zároveň nastavíme URL adresu, na ktorej bude služba zverejnená.
<wsdl:service name="movieReservationService">
<wsdl:port name="movieReservationPort"
binding="tns:movieReservationBinding">
<soap:address location="http://localhost:8080/movies/ws"/>
</wsdl:port>
</wsdl:service>
Sumár
Ak si zosumarizujeme veci definované vo WSDL, tak máme:
- typy – definujú syntax (štruktúru + dátové typy) prenášaných dát vo vnútri správ
- správy – definujú abstraktnú štruktúru prenášaných zásielok (požiadaviek a odpovedí).
- typ pre portu – definuje množinu operácií spolu so správami, ktoré sú v nich používané
- binding – predstavuje konkrétny protokol a konkrétnu reprezentáciu správ
- typ pre port + binding = port. Inak povedané, port získame dodaním protokolu k abstraktnej definícii typu portu.
- služba – zhromažďuje viacero portov
Kompletné WSDL
Kompletné WSDL si môžeme zobraziť ilustračne na obrázku:
Kompletné WSDL vyzerá nasledovne:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="urn:X-movies" targetNamespace="urn:X-movies" name="movie">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:X-movies">
<xsd:element name="movieReservationRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="date" type="xsd:date" />
<xsd:element name="numberOfTickets" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="movieReservationResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:int" />
<xsd:element name="reservedSeats" type="xsd:int" minOccurs="1" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
<wsdl:message name="movieReservationRequest">
<wsdl:part name="movieReservationRequestPart" element="tns:movieReservationRequest" />
</wsdl:message>
<wsdl:message name="movieReservationResponse">
<wsdl:part name="movieReservationResponsePart" element="tns:movieReservationResponse" />
</wsdl:message>
<wsdl:portType name="movieReservationPort">
<wsdl:operation name="movieReservation">
<wsdl:input message="tns:movieReservationRequest" />
<wsdl:output message="tns:movieReservationResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="movieReservationBinding" type="tns:movieReservationPort">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="movieReservation">
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="movieReservationService">
<wsdl:port name="movieReservationPort" binding="tns:movieReservationBinding">
<soap:address location="http://localhost:8080/movies/ws" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Literatúra
- WSDL Tutorial, W3Schools.org
- Understanding WSDL, Aaron Skonnard, Northface University, Microsoft 2003
- WSDL 1.1 – W3C Recommendation
- Introducing Design Patterns in XML
Schema
- popis XML schémy a návrhových vzorov (matrioška)
- Which style of WSDL should I use?, IBM DeveloperWorks.
- Java web services: Understanding and modeling WSDL 1.1, IBM DeveloperWorks