1. Vad är WSDL?
WSDL (Web Services Description Language) är ett språk som beskriver meddelandesyntax för webbtjänster. Det finns i dagsläget två versioner av WSDL:
- WSDL 1.1: Antogs av W3C 2001, detta är den standard som oftast används. Se specifikationen på
www.w3.org/TR/wsdl .
- WSDL 2.0: I skrivande stund ej antagen, men den 27 Mars 2006 publicerades "W3C Candidate Recommendation" på
www.w3.org/TR/wsdl20/ .
WSDL 2.0 innehåller så många förändringar jämfört med WSDL 1.1 att det måste ses som ett helt eget språk. WSDL 2.0 kommer inte att diskuteras på denna sida.
Nedan följer W3Cs beskrivning av WSDL 1.1:
- WSDL is an XML format for describing network services as a set of endpoints operating on messages containing either document-oriented or procedure-oriented information. The operations and messages are described abstractly, and then bound to a concrete network protocol and message format to define an endpoint. Related concrete endpoints are combined into abstract endpoints (services). WSDL is extensible to allow description of endpoints and their messages regardless of what message formats or network protocols are used to communicate.
En intressant punkt är att WSDL 1.1 är oberoende av nätverksprotokoll, något som vi ser senare på denna sida. WSDL 1.1 används dock i oftast tillsammans med följande nätverksprotokoll:
- SOAP 1.1
- HTTP GET/POST
- MIME
2. Element i WSDL
En WSDL-fil har definitions som rotelement. Direkt under toppnivån kan 7 typer av element existera:
- (0-n) documentation: Innehåller dokumentation.
- (0-n) import: Används för att importera andra WSDL-dokument. På detta sätt kan man återanvända WSDL-kod.
- (0-1) types: Innehåller egendefinierade datatyper.
- (0-n) message: Definierar formatet på meddelanden (ingånende eller utgående).
- (0-n) portType: (vanligtvis bara 1) Beskriver en mängd operationer och vilka meddenlanden (message) som de kan ta emot och skicka ifrån sig. Namnet "portType" är lite olyckligt, och i WSDL 2.0 har man bytt till "interface" vilket bättre beskriver detta element.
- (0-n) binding: (vanligtvis bara 1) Detta element binder gränssnittet "portType" mot ett nätverksprotokoll, vanligtvis SOAP 1.1. Operationerna binds mot sökvägar.
- (0-n) service: (vanligtvis bara 1) Detta element mappar "binding"-element mot en slutpunkt ("endpoint"). Om man använder SOAP över HTTP är slutpunkten en URL.
Alltså:
- portType beskriver vad som webbtjänsten innehåller (alltså vilka operationer som finns).
- binding beskriver hur man kommer åt operationerna.
- service beskriver var man kan hitta tjänsten.
Bilden nedan visar hur de olika elementen i en WSDL-fil hänger ihop:
3. Syntax för WSDL
Nedan beskrivs syntaxen för WSDL 1.1 (taget från W3C):
<wsdl:definitions name="nmtoken"? targetNamespace="uri"?>
<import namespace="uri" location="uri"/>*
<wsdl:documentation .... /> ?
<wsdl:types> ?
<wsdl:documentation .... />?
<xsd:schema .... />*
<-- extensibility element --> *
</wsdl:types>
<wsdl:message name="nmtoken"> *
<wsdl:documentation .... />?
<part name="nmtoken" element="qname"? type="qname"?/> *
</wsdl:message>
<wsdl:portType name="nmtoken">*
<wsdl:documentation .... />?
<wsdl:operation name="nmtoken">*
<wsdl:documentation .... /> ?
<wsdl:input name="nmtoken"? message="qname">?
<wsdl:documentation .... /> ?
</wsdl:input>
<wsdl:output name="nmtoken"? message="qname">?
<wsdl:documentation .... /> ?
</wsdl:output>
<wsdl:fault name="nmtoken" message="qname"> *
<wsdl:documentation .... /> ?
</wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="nmtoken" type="qname">*
<wsdl:documentation .... />?
<-- extensibility element --> *
<wsdl:operation name="nmtoken">*
<wsdl:documentation .... /> ?
<-- extensibility element --> *
<wsdl:input> ?
<wsdl:documentation .... /> ?
<-- extensibility element -->
</wsdl:input>
<wsdl:output> ?
<wsdl:documentation .... /> ?
<-- extensibility element --> *
</wsdl:output>
<wsdl:fault name="nmtoken"> *
<wsdl:documentation .... /> ?
<-- extensibility element --> *
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="nmtoken"> *
<wsdl:documentation .... />?
<wsdl:port name="nmtoken" binding="qname"> *
<wsdl:documentation .... /> ?
<-- extensibility element -->
</wsdl:port>
<-- extensibility element -->
</wsdl:service>
<-- extensibility element --> *
</wsdl:definitions>
|
Nu är det dags för ett exempel..
4. Ett exempel på WSDL: Calculator
Pe denna sida ska vi titta på WSDL-filen för en applikation av Hello World-karaktär, nämligen Calculator.
- OBS: Calculator är medvetet byggd så enkel som möjligt, därför har den t.ex. inget types-element.
Webbtjänsten Calculator implementerar en enkel miniräknare som kan anropas med SOAP över HTTP. Miniräknaren har i detta fall bara har två operationer, multiplikation och division. Nedan visas hur filen Calculator.wsdl ser ut:
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="Calculator"
targetNamespace="http://www.your_domain.com/axis/services/Calculator.wsdl"
xmlns:tns="http://www.your_domain.com/axis/services/Calculator.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:cal="http://www.your_domain.com/ns/Calculator.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/" >
<!-- Types -->
<types>
<xsd:schema targetNamespace="http://www.your_domain.com/ns/Calculator.xsd" >
<xsd:complexType name="MultiplicationRequestType" >
<xsd:all>
<xsd:element name="multiplicand" type="xsd:int" />
<xsd:element name="multiplier" type="xsd:int" />
</xsd:all>
</xsd:complexType>
<xsd:complexType name="DivisionRequestType" >
<xsd:all>
<xsd:element name="dividend" type="xsd:int" />
<xsd:element name="divisor" type="xsd:int" />
</xsd:all>
</xsd:complexType>
</xsd:schema>
</types>
<!-- Messages -->
<message name="MultiplicationRequest">
<part name="multiplicationRequestPart" type="cal:MultiplicationRequestType"/>
</message>
<message name="MultiplicationResponse">
<part name="multiplicationReturn" type="xsd:int"/>
</message>
<message name="DivisionRequest">
<part name="divisionRequestPart" type="cal:DivisionRequestType"/>
</message>
<message name="DivisionResponse">
<part name="divisionReturn" type="xsd:float"/>
</message>
<message name="DivideZeroException">
<part name="divideZeroError" type="xsd:string"/>
</message>
<!-- Port types -->
<portType name="CalculatorPortType">
<operation name="multiplication" >
<input message="tns:MultiplicationRequest" name="multiplicationRequest"/>
<output message="tns:MultiplicationResponse" name="multiplicationResponse"/>
</operation>
<operation name="division" >
<input message="tns:DivisionRequest" name="divisionRequest"/>
<output message="tns:DivisionResponse" name="divisionResponse"/>
<fault message="tns:DivideZeroException" name="divideZeroException"/>
</operation>
</portType>
<!-- Bindings -->
<binding name="CalculatorSoapBinding" type="tns:CalculatorPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="multiplication">
<soap:operation soapAction="http://www.your_domain.com/axis/services/Calculator/Multiplication"/>
<input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://www.your_domain.com/ns/Calculator.xsd" />
</input>
<output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://www.your_domain.com/ns/Calculator.xsd" />
</output>
</operation>
<operation name="division">
<soap:operation soapAction="http://www.your_domain.com/axis/services/Calculator/Division"/>
<input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://www.your_domain.com/ns/Calculator.xsd" />
</input>
<output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://www.your_domain.com/ns/Calculator.xsd" />
</output>
<fault>
<soap:fault use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://www.your_domain.com/ns/Calculator.xsd" />
</fault>
</operation>
</binding>
<!-- Services -->
<service name="CalculatorService">
<port binding="tns:CalculatorSoapBinding" name="Calculator">
<soap:address location="http://localhost:8080/axis/services/Calculator"/>
</port>
</service>
</definitions>
|
I avsnittet Webbtjänster med Java EE kan du se hur Calculator.wsdl används för att bygga en fungerande webbtjänst:
www.programmera.net/jax/Exemplet Calculator ( med Axis ) .
5. definitions
definitions är rotelementet för WSDL-trädet. definitions har två valbara attribut:
- name: Ett namn på tjänsten som beskrivs
- targetNamespace: Ett namnutrymme ("namespace"). Beskrivs av en URI som ej får vara relativ.
definitions-elementet innehåller vanligtvis ytterliga XML "namespace" (xmlns:) som vi ser i vårt exempel:
<definitions name="Calculator"
targetNamespace="http://www.your_domain.com/axis/services/Calculator.wsdl"
xmlns:tns="http://www.your_domain.com/axis/services/Calculator.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:cal="http://www.your_domain.com/ns/Calculator.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/" >
...
</definitions>
|
I detta exempel har de ytterligare namnutrymmena följande funktion:
- tns: (This NameSpace) Används för att referera till det egna WSDL-dokumentet. I exemplet Calculator används detta namnutrymme i portType-elementet (för att beskriva varifrån man hämtar message-elementen) och i binding-elementet (för att beskriva var man hittar portType-elementet).
- xsd: Används i types-elementet och message-elementet för att definiera datatyper (se avsnittet nedan).
- cal: Används i message-elementet för att komma åt de egendefinierade datatyperna i types-elementet.
- soap: Används i binding-elementet och service-elementet för att binda definitionen till SOAP-protokollet.
- "default namespace": Eftersom det sista namnutrymmet inte har något prefix blir detta "default namespace" för WSDL-dokumentet. Ofta döper man detta namnutrymme med prefixet wsdl för att markera att det rör sig om WSDL-element.
6. Namnutrymmet xsd
xsd är ett namnutrymme som innehåller färdiga typer, vilka används på följande sätt:
- Använd direkt i message-elementen då du vill skicka enkla parametrar som in och utvärde till din operation.
- Använd som byggstenar för att bygga större och mer komplexa typer, som du sedan använder i message-elementen.
Tabellen nedan visar vilka standardtyper som xsd innehåller som kan använas för att bygga mer komplexa typer:
Datatyper i xsd | | Beskrivning |
string | | Vanlig sträng av tecken. |
integer | | Typen integer definieras som en sträng av siffror som kan vara hur lång som helst. |
long | | - |
int | | - |
short | | - |
byte | | - |
decimal | | - |
double | | - |
float | | - |
Boolean | | - |
date | | - |
dateTime | | - |
QName | | - |
hexBinary | | - |
base64Binary | | - |
7. types
I types-elementet kan du definiera egna XML-typer som du senare kan använda i message-elementen.
- OBS: Det är viktigt att förstå att den kod som ligger innanför types-elementet är uteslutande "XML Schema"-kod. Man kan se types-elementet som en "inline"-definition av ett "XML Schema".
I exemplet Calculator.wsdl har vi skapat typer för inparametrarna till våra 2 operationer multiplication och division:
<types>
<xsd:schema targetNamespace="http://www.your_domain.com/ns/Calculator.xsd" >
<xsd:complexType name="MultiplicationRequestType" >
<xsd:all>
<xsd:element name="multiplicand" type="xsd:int" />
<xsd:element name="multiplier" type="xsd:int" />
</xsd:all>
</xsd:complexType>
<xsd:complexType name="DivisionRequestType" >
<xsd:all>
<xsd:element name="dividend" type="xsd:int" />
<xsd:element name="divisor" type="xsd:int" />
</xsd:all>
</xsd:complexType>
</xsd:schema>
</types>
|
I detta exempel har alla element-taggar type-attribut av kategorin simpleType som hämtas från namnutrymmet xsd (se ovan). För att typerna som definierats ovan ska gå att använda måste targetNamespace mappas mot ett prefix i definitions-elementet. I vårt fall mappas targetNamespace mot prefixet cal (se ovan).
- OBS: targetNamespace-attributet ser ut att referera till en existerande fil (http://www.your_domain.com/ns/Calculator.xsd), men denna fil finns inte. Det är bara ett fiktivt namn som syftar till att göra WSDL-koden mer begriplig.
Prefixet cal används senare i message-elementen.
8. types (alternativ 2)
Ofta importerar man datatyperna från ett externt XML Schema. Detta är bra om din organisation redan har definierat sina datatyper i XML Schema och man vill återanvända dessa datatyper på ett smidigt sätt. Se exemplet nedan:
<types>
<xsd:schema>
<xsd:import namespace="http://www.your_domain.com/ns/invoice"
schemaLocation="http://www.your_domain.com/schema/invoice.xsd" />
<xsd:import namespace="http://www.your_domain.com/ns/order"
schemaLocation="http://www.your_domain.com/schema/order.xsd" />
</xsd:schema>
</types>
|
I detta exempel ser vi att vi inte använder något targetNamespace i schema-taggen. Istället definierar varje import-element sitt eget namnutrymme. Såhär ska man bara göra då schema endast innehåller importsatser.
9. message
Med detta element definierar man utgående, ingående och fel-meddelanden till operationerna i webbtjänsten. Ett meddelande kan innehålla en eller flera part-element. Ett part-element har två attribut med förvirrande lika funktion, nämligen type och element. Idén är att man bara ska använda antingen type eller element för varje enskilt part-element. Nedan beskrivs attributen för part närmare:
Attribut för message/part | | Beskrivning |
name | | Ett namn som beskriver part-elementet. Måste vara unikt bland sina syskon. |
type | | Detta attribut ska användas då webbtjänsten är dataorienterad (överför parametrar i form av data). Refererar till en datatyp, antingen en simpleType eller en complexType som är definierad i XML Schema. |
element | | Detta attribut ska användas då webbtjänsten är dokumentorienterad (överför dokument). element-attributet refererar också till en datatyp som definieras i ett XML Schema, men med skillnaden att attributets värde (själva datatypen) måste vara definierad innuti types-elementet för WSDL-filen. När man använder attributet element tvingar man transportprotokollet att skicka part-elementet i ett separat paket (för SOAP innebär detta att datatypen ligger ensam i soap:Body). |
Se exemplet Calculator.wsdl:
<message name="MultiplicationRequest">
<part name="multiplicationRequestPart" type="cal:MultiplicationRequestType"/>
</message>
<message name="MultiplicationResponse">
<part name="multiplicationReturn" type="xsd:int"/>
</message>
<message name="DivisionRequest">
<part name="divisionRequestPart" type="cal:DivisionRequestType"/>
</message>
<message name="DivisionResponse">
<part name="divisionReturn" type="xsd:float"/>
</message>
<message name="DivideZeroException">
<part name="divideZeroError" type="xsd:string"/>
</message>
|
I detta exempel används bara attributet type (eftersom vår webbtjänst är dataorienterad). Vi ser att vi använder två olika sorters datatyper:
- xsd:int Denna datatyp hämtas från namnutrymmet xsd-schemat (som beskrivs i avsnittet ovan).
- cal:MultiplicationRequestType: Denna datatyp hämtas från namnutrymmet cal som deklarerades i den egna WSDL-filen (se avsnittet om types ovan).
10. portType
Detta element representerar själva gränssnittet för en webbtjänst. Vanligtvis används bara ett portType-element i en WSDL-fil, det är helt enklet god sed att separera olika gränssnitt i olika filer. Vi tittar på ett exempel på hur ett portType-element kan se ut:
<portType name="CalculatorPortType">
<operation name="multiplication" parameterOrder="multiplicand multiplier">
<input message="tns:MultiplicationRequest" name="multiplicationRequest"/>
<output message="tns:MultiplicationResponse" name="multiplicationResponse"/>
</operation>
<operation name="division" parameterOrder="dividend divisor">
<input message="tns:DivisionRequest" name="divisionRequest"/>
<output message="tns:DivisionResponse" name="divisionResponse"/>
<fault message="tns:DivideZeroException" name="divideZeroException"/>
</operation>
</portType>
|
Vi ser att själva portType-elementet bara har ett attribut:
Attribut | | Beskrivning |
name | | I detta fall CalculatorPortType, vilket följer namnstandarden. |
Vi ser att vi bara har operationer med både input och output-element för våra operationer. Det går även att definera operationer som tar bara input eller bara output, men input och output är trots allt det vanligaste. Vi ser att operationen division dessutom har ett fault-element. Detta element visar att denna metod kan kasta en speciell sorts undantag, nämligen divideZeroException.
- OBS: För att peka ut vilket meddelande som används för operationerna används namnutrymmet tns som pekar tillbaka på det egna WSDL-dokumentet. Man säger alltså: "Leta i detta dokument efter ett meddelande som heter multiplicationRequest". Detta är det vanligaste sättet att hitta till definitionerna av message-elementen, men WSDL är öppet för att man ska kunna leta efter meddelandedefinitioner i andra namnutrymmen.
11. binding
Elementet binding ansvarar för att binda den abstrakta beskrivningen av webbtjänsten som angavs i portType mot ett nätverksprotokoll.
- Ett portType-element kan ha ett eller flera binding-element knutet till sig, men vanligtvis används bara ett binding-element.
I exemplet Calculator.wsdl binds portType-elementet mot SOAP över HTTP. Hur man gör detta beskrivs på sidan
SOAP binding för WSDL .
12. service
Service innehåller en grupp port-element (vanligtvis endast ett) som alla mappar ett binding-element mot en slutpunkt ("endpoint").
- OBS: I vårt exempel är en slutpunkt URL, eftersom vi använder SOAP/HTTP som transportprotokoll. Om man använder något annat protokoll kan en slutpunkt vara t.ex. en epostadress.
I vårt exempel ser service-taggen ut såhär:
<service name="CalculatorService">
<port binding="tns:CalculatorSoapBinding" name="Calculator">
<soap:address location="http://localhost:8080/axis/services/Calculator"/>
</port>
</service>
|
Eftersom vi bara kommer att köra Calculator-applikationen lokalt mappar vi den mot localhost:8080. En riktig applikation bör mappas mot en publik URL.