C# - A SOAP protokoll áttekintése

forráskód letöltése
A Webes kommunikáció egyre jobban terjedő protokollja a SOAP protokoll. A .NET Framework számtalan lehetőséget biztosít osztályain keresztül ahhoz, hogy alkalmazásaink gördülékenyen vehessék igénybe a SOAP üzenetváltást. Annak érdekében, hogy jól használhassuk a protokoll által definiált kommunikációs módot, ismernünk kell a leglényegesebb kapcsolódó információkat. Elméleti cikkünkben a SOAP protokoll alapjait mutatjuk be, figyelmet fordítva az üzenet felépítésére.
Mellékelt példa megnyitása előtt szükséges egy Service nevű virtuális könyvtár létrehozása, mely a példa mappájában található Service alkönyvtárra mutat. Ehhez nyissa meg a mappa Tulajdonság ablakát és itt a Webmegosztás lapon engedélyezze a mappa megosztását olvasási és parancsfájlok futtatási jogával.
A kliens alkalmazás futtatása előtt gondoskodnunk kell arról, hogy a Web szerver virtuális mappájában engedélyezzük a névtelen hozzáférést.
A Web szolgáltatások megfelelő interfésszel rendelkező, az alkalmazások által a Weben keresztül könnyen igénybe vehető funkciókat tartalmaznak. Legfontosabb jellemzőjük, hogy felépítésüket, működésüket szabványok írják elő, így az Internet heterogén közegében is használhatóak.
A technológia új, azonban a legelterjedtebb platformokra már megszülettek az implementációk. Említést érdemel így a SUN Java-hoz, és az IBM Linux-hoz készült implementációja. Mivel ezek között a platformok között a kommunikáció magas szinten történik – összetett adatszerkezetek, objektumok, struktúrák átadása - így a Web szolgáltatások megjelenése igen nagy technikai előrelépésként jellemezhető.
SOAP protokoll
A SOAP (Simple Object Access Protokol) ajánlás – melyet a Microsoft és az IBM közösen dolgozott ki -, három fő részből áll:
  • Definiál egy általános protokollt XML dokumentumok továbbítására. Ez B2B kommunikációban is jól használható. A SOAP üzenet két elemet tartalmaz mindössze: kérés üzenet és válasz üzenet. A SOAP ezekhez definiál egy borítékot (envelope), melyben elhelyezhetjük a hasznos információkat.
  • Az ajánlás második része leír egy szabványos adatreprezentációt, mely tulajdonképpen annak a módszernek a megfogalmazása, ahogyan az egyszerű és összetett adatszerkezeteinket XML formátumban leírhatjuk. A kommunikációban nem szükséges az üzeneteket ennek megfelelően leírni, azonban mivel mind a kliensnek, mind a szervernek ismernie kell az adatok formátumát, ezért heterogén környezetben célszerű a szabványos megoldást választani.
  • A harmadik rész definiálja az első részben leírt protokollnak a HTTP-re való leképezését.
SOAP boríték jellemzői
A SOAP borítékban definiált tag-ek a http://schemas.xmlsoap.org/soap/envelope/ névtérben vannak definiálva. Az üzenet gyökéreleme az <Envelope> elem. Opcionálisan szerepelhet ezen belül a <Header> elem, melyben tetszőleges elemeket továbbíthatunk, így lehetővé válik a protokoll későbbi kiterjesztése. Kötelezően szerepelnie kell viszont a <Body> elemnek, melyben a dokumentumok továbbításra kerülnek. Lássunk egy példát:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="...”>
  <SOAP-ENV:Header>
    ...
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <owntag:Document xmlns:owntag="www.softwareonline.hu">
      ...
    </owntag:Document>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Használt adattípusok
Az adattípusokat a SOAP ajánlás külön nem definiálja, hanem változatlan formában átveszi az XML sémákat leíró ajánlásból. Ezek mellett lehetőségünk van összetett adattípusok továbbítására, mint a tömbök, vagy struktúrák (hasonlóan az osztályokhoz, ahol csak az állapotot kell továbbítani). A struktúrák mezők mellett tartalmazhatnak referenciákat más struktúrákra is. Ennek megvalósítását a SOAP szerializáció teszi lehetővé, mely elhelyezi az összes struktúrát a dokumentumban, mely fellelhető a hivatkozási láncban, valamint leírja a köztük fennálló kapcsolatokat. Egy példa:
<Writer id="W1">
  <name>Szilvási Lajos</name>
</Writer>
<Book>
  <title>A néma</title>
  <author href="#W1"/>
</Book>
Látható, hogy szerepel egy írót leíró struktúra, melynek azonosítója W1. A második struktúra egy könyvet ír le, melynek írójára az iménti azonosítóval hivatkozhatunk. A SOAP szerializáció a körhivatkozásokat is képes kezelni.
HTTP, mint a SOAP alapja
A HTTP protokollt arra tervezték valamikor, hogy a kliens HTML oldalakat legyen képes letölteni a Web szerverekről. A kliens többféleképpen juttathatja el kérését a távoli kiszolgálóhoz annak érdekében, hogy a kért információt reprezentáló HTML oldalt megkapja. Ennek egyik módja a GET kérés, melynek lényege, hogy egyetlen fontos paramétere a HTML oldal (erőforrás) helyét megadó URL. A kérés eljuttatására így az URL-ben van lehetőség.
Ennek két nagy hátránya van:
  • Az URL mérete nem haladhatja meg az 1024 byte-ot.
  • Az URL-ben küldött információ minden esetben megjelenik a kliens böngészőjének ablakában.
A fenti problémák megoldására fejlesztették ki a POST kérést. A küldött információ itt már nem az URL-ben utazik, hanem az üzenet fejlécében. A POST kérést alapvetően a HTML-űrlapok miatt biztosítja a HTTP, így a szabvány leírja azt is, hogy miként kell az adatokat az üzenetben reprezentálni. Ennek módja GET és POST esetében is az, hogy minden űrlapelem nevéhez egy „=” jel beiktatásával hozzáfűzzük az értékét, és az ilyen elemeket a „&” jel segítésével választjuk el egymástól.
Az egyszerűbb Web szolgáltatások funkciói elérhetők GET, vagy POST kérésekkel is, hiszen az űrlapelem neve és értéke helyett használhatjuk az egyes paraméterek nevét és értékét is. Összetett adatszerkezetek esetén ez a módszer nem célravezető, itt a SOAP kérést kell használnunk. A SOAP kérés is az egyszerű POST kérésre épül, itt azonban más az említett SOAP borítékban továbbítódnak az adatok (az URL, és a fejlécmező helyett). A válasz üzenet is egy SOAP boríték, mely vagy a függvény visszatérési értékét, vagy hibaüzenetet tartalmaz.
Felfedezhető, hogy az egyszerűbb POST kérésre való leképezés miatt a Web szolgáltatások szerver-oldali script-ekkel (ASP), illetve szerver-modulokkal (ISAPI) jól illeszthetők a Web szerverhez.
WSDL: interfészek leírására
A Web szolgáltatások a COM objektumoktól eltérő, XML alapú interfészleíró nyelvet használnak. Ennek neve WSDL (Web Services Description Language). Egy Web szolgáltatás interfészét így egyetlen XML dokumentum (WSDL állomány) írja le, a szolgáltatás igénybevételéhez így mindössze erre az egy dokumentumra van szükségünk. Ennek URL-je a mellékelt szerviz alkalmazás esetén a következő:
http://localhost/Service/WSErvice.asmx?WSDL
Lássuk a dokumentum legfontosabb elemeit.
Az ajánlás valamennyi eleme a „http://schemas.xmlsoap.org/wsdl/” névtérbe tartozik. A WSDL dokumentum gyökéreleme – mivel interfészleíró nyelv lévén különböző definíciókat tartalmaz - a <definitions>. A mellékelt Web szerviz alkalmazás esetén a következőképpen jelenik meg:
<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="http://tempuri.org/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/">
A fenti elemen belül elsőként az üzenetben előforduló összetett adattípusok leírására kerül sor. Erre a <types> elem szolgál, melyben az XML ajánlásnak megfelelően, szerepelnek az egyes típusok definíciói. Egy típus leírása a következőképpen fest a mellékelt szervizben:
<types>
  <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
A metódus definíciója látható az alábbiakban:
    <s:element name="AddNumbers">
      <s:complexType>
        <s:sequence>
A paraméterek:
          <s:element minOccurs="1" maxOccurs="1" name="a" type="s:int" /> 
          <s:element minOccurs="1" maxOccurs="1" name="b" type="s:int" /> 
        </s:sequence>
      </s:complexType>
    </s:element>
    ...
  </s:schema>
</types>
Ezt követően kerülhet sor az üzenet leírására a <message> elemekben, melyeket a „name” attribútumban elhelyezett nevükkel tudunk azonosítani. Az elemek tartalmaznak egy <part> elemet, mely kérés esetén a függvény, válasz esetén a visszatérési érték nevét és típusát írják le. Ezek lehetnek már definiált összetett típusok, vagy valamely XML sémának megfelelő egyszerű típus. Szervizünk esetén ez a következő:
<message name="AddNumbersHttpGetIn">
  <part name="a" type="s:string" /> 
  <part name="b" type="s:string" /> 
</message>
Annak érdekében, hogy a kérés és válasz üzenetek találkozzanak, össze kell őket párosítanunk. Erre szolgál a <portType> elem, melynek <operation> eleme tartalmazza a megfelelő párosításokat.
<portType name="WServiceSoap">
  <operation name="AddNumbers">
    <input message="s0:AddNumbersSoapIn" /> 
    <output message="s0:AddNumbersSoapOut" /> 
  </operation>
</portType>
A definíció teljessége érdekében meg kell említeni, hogy a szolgáltatást különböző URL-eken és különböző protokollokon is elérhetővé tehetjük (HTTP GET, HTTP POST, SOAP). Ennek érdekében a definíció tartalmaz egy <service> elemet, mely leírja, hogy a fentiek közül mely protokollok esetén milyen URL-en, és milyen módon érhető el a szerviz. Az egyes jellemzőket a <port> elem tartalmazza. Elsőként lássuk SOAP esetén:
<service name="WService">
  <port name="WServiceSoap" binding="s0:WServiceSoap">
    <soap:address location="http://localhost/Service/WService.asmx" /> 
  </port>
Majd HTTP GET esetén:
  <port name="WServiceHttpGet" binding="s0:WServiceHttpGet">
    <http:address location="http://localhost/Service/WService.asmx" /> 
  </port>
És végül HTTP POST-ra:
  <port name="WServiceHttpPost" binding="s0:WServiceHttpPost">
    <http:address location="http://localhost/Service/WService.asmx" /> 
  </port>
</service>
Látható, hogy alapértelmezésben a szerviz elérésére mindhárom módszer esetén ugyanazt az URL-t adtuk meg.
Végül a definiált portType-okat és portokat össze kell párosítani egy <binding> tag segítségével. Ugyanitt adhatjuk meg az összes protokollra jellemző adatot is, például a SOAP esetében azt, hogy az melyik alacsonyabb szintű protokollra épül.
Megvalósítás .NET-ben
A Web szolgáltatásokat a HTTP protokoll használata miatt szerver oldalon rendszerint egy Web szerver használatával valósítják meg. Egy függvény visszatérési értékét leíró XML dokumentum előállítása bonyolultabb feladat, mint egy egyszerű HTML dokumentum visszaadása, hiszen annak tartalma a kapott függvény paraméterektől függ. Azonban már a dinamikus tartalmú weboldalak előállítására is léteznek különböző technológiák, melyek változtatás nélkül felhasználhatóak a Web szolgáltatások esetében.
Ennek megfelelően, amikor a Visual Studio .NET segítségével létrehozunk egy Web szolgáltatást, akkor valójában egy speciális ASP.NET Web-alkalmazás jön létre. Jellegzetessége, hogy működése GET és POST paraméterekkel módosítható.
Kliens oldalon a Visual Studio .NET a távoli eljárások egyszerű elérésének érdekében egy proxy osztályt hoz létre. Ez egy olyan osztály, melynek metódusai teljesen megegyeznek azokkal, melyeket a Web szolgáltatás tartalmaz, implementációja azonban olyan, hogy minden hívás esetén a paramétereket továbbítja a távoli szerver felé, majd miután az eljárás ott lefutott, és visszatérési értéke visszakerült a kliens oldalra, a proxy osztály azt visszaadja. Ennek a módszernek köszönhetően a felhasználó egy Web szolgáltatást pontosan úgy használhat, mint egy helyi osztályt, ami a fejlesztés hatékonysága és a forráskód átláthatósága szempontjából egyaránt előnyös.
A következőkben a forráskód-szintű kapcsolat kialakításának módszerét vizsgáljuk meg. A művelet ott kezdődik, hogy a szerviz alkalmazás WSDL állományát kell valamilyen módon a szervizt igénybevevő alkalmazáshoz adnunk. Ennek leggyorsabb és legegyszerűbb módja, amikor egy Webes referenciaként adjuk azt meg. Ebben az esetben egy URL-t kell megadnunk, melyről egyszerűen letöltődik a szerviz WSDL dokumentuma. Ennek részleteivel cikkünkben nem foglalkozunk, bővebben erről a C# Software Online Web Reference használata WebService eléréséhez című cikkünkben olvashat (2. évfolyam 38. szám).
Bármilyen Web szolgáltatást kívánunk igénybe venni, a WSDL állomány átkerül a kliens-oldalra. A Visual Studio .NET létrehozza a web szolgáltatáshoz tartozó proxy osztályt. Ehhez minden információt kizárólag az imént letöltött WSDL állományból olvas ki. A proxy egy névtérbe kerül, melynek nevét azonban a tulajdonságok között megváltoztathatjuk. A proxy osztályban látható, hogy az eredeti metódusok mellett két-két másikat is tartalmaz. Az egyik nevében az eredeti eljárás neve előtt a Begin míg a másikban az End szerepel. Ezek segítségével ún. aszinkron hívásokat hajthatunk végre. Megtehetjük ugyanis azt, hogy meghívjuk a metódust (Begin...), azonban annak futását nem várjuk meg, hanem eredményét később, a másik metódus (End...) segítségével kérdezzük le.
A kliens-alkalmazásban a következő lépésekkel hívható meg a Web szolgáltatás:
  • Az alkalmazás létrehoz a proxy osztályból egy példányt és meghívja annak metódusát.
  • A proxy objektum implementációja olyan, hogy a megkapott paramétereket szerializáció segítségével XML formátumban egy SOAP üzenet belsejében helyezi el. A szerializáció segítségével ugyanis a memóriában lévő objektumainkat a közöttük fennálló kapcsolódásokkal együtt elmenthetjük egy állományba, majd azokat a deszerializáció segítségével visszatölthetjük onnan. A megvalósítás óriási előnye, hogy az osztályokban semmilyen egyedi implementációra sincs szükség, azaz a folyamat teljesen automatikusan megy végbe.
  • A SOAP üzenetet a HTTP protokoll felett a proxy továbbítja a szerver-oldalra.
  • A szerver oldalon futó Webes alkalmazásunk a megkapott POST paraméterek alapján meghatározza, hogy mely eljárást kell lefuttatnia, és deszerializáció segítségével az XML formátumban lévő paramétereket objektumok formájában felépíti a memóriában.
  • A függvénynév és a paraméterek nevének ismeretében a Web szolgáltatás lefuttatja a megfelelő eljárást.
  • A függvény visszatérési értékének felhasználásával egy ismételt szerializáció eredményeképpen létrejön a SOAP válasz üzenet.
  • A SOAP üzenetet tartalmazó XML dokumentum a kérésre adott válaszként visszakerül a kliens-oldalra.
  • A proxy a megkapott információkat egy végső deszerializáció segítségével feldolgozza, majd objektumok formájában visszaadja azokat az alkalmazásnak.
A Web szolgáltatások esetén figyelmet kell fordítani azok hatékonyságára, hiszen az Internet lassú átviteli közeg. A visszatérési értékekben minimálisra kell csökkenteni a redundanciát, egyébként sebesség visszaesést tapasztalhatunk. Fontos megemlíteni, hogy az egyes kulcsszavakat megfelelően kell használnunk. A REF kulcsszó használata azt eredményezi, hogy az átadott paraméter – a helyi hívásoktól eltérően - nem referenciaként kerül át a túloldalra, hanem értéke a kérésbe és a válasz üzenetbe is belekerül. A csak az adatok visszaadására használt paramétereket az OUT kulcsszó segítségével érdemes definiálni.