Tartalmi kivonat
Szegedi Tudományegyetem Informatikai Tanszékcsoport RESTful web-szolgáltatások készítése WCF-ben Diplomamunka Készítette: Témavezető: Pótári Tamás Dr. Alexin Zoltán Programtervező informatikus MSc potari.tamas@gmailcom Egyetemi adjunktus alexin@inf.u-szegedhu Szeged 2011 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Feladatkiírás Napjaink kisvállalkozásai a kereskedelmi szférában egy webshop-pal, illetve egy ettől független – vagy szerencsésebb esetben ezzel összhangban lévő – számlázó és raktárkészlet nyilvántartó alkalmazással kezdik tevékenységüket az internet nyújtotta végtelen piacon. Idővel viszont a rendelkezésre álló informatikai megoldás kevésnek bizonyul számukra. Így szükségük lesz egy komolyabb kereskedelmi rendszerre, mellyel bővíthetik lehetőségeiket a mindennapi versenyben. Erre a problémára az ERP-k nyújtanak megoldást, melyekből rengeteg található a piacon. A
diplomamunkám egy tipikus problémakörre koncentrál, ami legtöbbször egy ERP bevezetése előtt, vagy éppen bevezetés közben szokott jelentkezni. A valamirevaló ERP-k rendelkeznek webes felülettel, hétköznapian fogalmazva: webshop-pal. De megeshet, hogy ez a webshop nem elégíti ki a megrendelők igényeit, ezért át szeretnék azt alakíttatni az ERP szállítójával. Sőt felmerülhetnek olyan igények is, hogy az új ERP rendszert „összekössék” a bevált, vásárlók számára jól ismert, korábbi webshop-jukkal. Az összekapcsolás rendkívül kényes kérdés. Miután tisztázták a felek a két (vagy több) rendszer kommunikációjának sarokpontjait, irányát, gondoskodniuk kell a technológiai akadályokról is. A két rendszer ugyanis nagy valószínűséggel egymástól teljesen eltérő platformon működik, így a fejlesztők kommunikációja is nehézkes. De mégis egy elvi kérdés a legnyugtalanítóbb, miszerint egy ERP szállító, a termékük
adatbázisát és üzleti logikai rétegét nem szívesen teszi közzé egy másik cég számára. A megoldás egy API Diplomamunkám célja egy meglévő és működő, webes kereskedelmi rendszer kibővítése web-szolgáltatásokkal az imént említett API megvalósítása érdekében. Így könnyedén össze lehet kötni más rendszerekkel, a korábban említett problémákat áthidalva. Mivel a kereskedelmi rendszer Microsoft .NET alapú, ezért a WCF technológiával implementálom az API-t, mint RESTful szolgáltatások halmaza. Bemutatom a szóban forgó kereskedelmi rendszer főbb szolgáltatásait. Részletekbe menően szemléltetem a modellezés, az implementálás, a beüzemelés és a tesztelés minden lépését. A fejlesztési folyamat rendkívül széles technológiai látókört igényel; a REST világ, a Microsoft WCF, a Microsoft SQL Server, IIS7 és a .NET komponensorientált programozás, csakhogy néhány témakört említsek Ezek legtöbbjét, szükségszerűen
be is mutatom, sőt elismert fejlesztők és szakértők tervezési módszereit, technikáit alkalmazom. Betekintést biztosítok a REST és RESTful világba majd a WCF érintett szegmensébe. 2 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Az elkészített web-szolgáltatásokat akár más szoftverrendszerek is felhasználhatják, szemléltetni is fogom ennek lehetőségeit. Továbbá a kereskedelmi rendszer az új API segítségével nagy előnyre tesz szert az ERP-k piacán, mivel még napjainkban is alig-alig találkozni jó megoldásokkal. 3 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Tartalmi összefoglaló • A téma megnevezése: RESTful web-szolgáltatások készítése WCF-ben • A megadott feladat megfogalmazása: A cél, egy meglévő és működő, Microsoft .NET alapú webes kereskedelmi rendszer kibővítése web-szolgáltatásokkal, és ezen keresztül a RESTful szolgáltatások előállítási lehetőségeinek
bemutatása Microsoft WCF-ben. • A megoldási mód: A RESTful architektúra stílus útmutatóját követve történik a tervezés, a kivitelezés pedig Microsoft WCF-ben, C# nyelven. A kibővítendő kereskedelmi rendszer komponensei lettek újrahasznosítva. A tesztelés, beüzemelés és optimalizálás szintén Microsoft eszközökkel zajlott. • Alkalmazott eszközök, módszerek: A fejlesztés Microsoft .NET 4-ben, C# nyelven zajlott WCF WebHttp szolgáltatásokkal A fő fejlesztőeszköz a Visual Studio 2010 (Ultimate) volt, továbbá a tesztelés is a Visual Studio Web Performance Test megoldásaival valósult meg. A dolgozatban bemutatásra került a kibővített alaprendszer érintett szegmense is. A RESTfult architektúra stílus erőforrásorientált tervezési útmutatója lett alkalmazva A módszerek és minták javarésze elismert szakírók által alátámasztottak, továbbá az egyedi ötletek részletesen be lettek mutatva. • Elért eredmények: A kívánt
RESTful szolgáltatás elkészült, dokumentálva lett, átesett egy automatizált tesztelésen, telepítve és teljesítmény tesztek útján optimalizálva lett. Az elkészült terméket a dolgozat születése közben már felhasználta egy másik web-es szoftver. A kialakítás és a kommunikáció röviden be lett mutatva. • Kulcsszavak: REST és RESTful szolgáltatások, Microsoft WCF WebHttp szerveroldali megoldások, Data Contract sorosítás, API kulcsos hitelesítés, gyorsítótárazás, komponens-orientált programozás, Visual Studio Web Performance Test, IIS 7.5, ASPNET 4, SQL Server 2008 4 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Tartalomjegyzék 1. BEVEZETÉS 7 2. A REST VILÁG 8 2.1 A SOAP 8 2.2 A REST 9 2.3 A RESTFUL-SÁG 9 2.4 REST A GYAKORLATBAN 10 2.41 Az erőforrások meghatározása 10 2.42 Az URI-k megtervezése 11 2.43 Az egységes interfész implementálása 12 2.44 A reprezentáció megtervezése 13 2.45
Kapcsolatok kialakítása 15 2.5 TERVEZÉSI MINTÁK, SPECIÁLIS MEGOLDÁSOK 15 2.51 Tervezési minták erőforrásokhoz 15 3. A WCF RESTFUL PROGRAMOZÁSI MODELLJE 17 3.1 WCF A SZERVEROLDALON 17 3.2 A WEBHTTP SZOLGÁLTATÁSOK 18 3.21 A WCF WebHttp szolgáltatások adottságai fejlesztői szempontból 19 3.3 A DATA CONTRACT SOROSÍTÁS 20 3.31 Deklaratív programozás 21 3.32 Öröklés és interfészek implementációi 22 3.33 Hivatkozások és körkörös hivatkozások 22 3.34 Verzió tolerancia 22 3.35 Null és üres értékek 23 3.36 Gyűjtemények 24 3.37 Néhány sajátosság 24 4. AZ ALAPRENDSZER ISMERTETÉSE 26 4.1 AZ ÉRINTETT SZERELVÉNYEK ISMERTETÉSE 27 4.2 A RENDSZER NÉHÁNY FONTOS TULAJDONSÁGA 29 4.21 Interfészek és implementációik 29 4.22 Komponens alapú biztonság 30 4.23 A lokalizáció 31 4.24 Példányosítás, mentés, törlés és listázás 31 5. KIVITELEZÉS 33 5.1 A REST WCF PROJEKT LÉTREHOZÁSA 33 5.2 AZ ERŐFORRÁSOK MEGHATÁROZÁSA
33 5.21 Az érintett témakörök 34 5.22 Kompozitok 36 5.23 A közös interfész 38 5.3 AZ URI-K MEGTERVEZÉSE 38 5.31 Az URI template-ek megtervezése 39 5.32 Az URI template-ek implementálása 40 5.4 AZ EGYSÉGES HTTP INTERFÉSZ IMPLEMENTÁLÁSA 41 5.5 A REPREZENTÁCIÓ MEGTERVEZÉSE 43 5.51 A Data Contract dekoráció és részleges objektumok 44 5.52 A kívánt reprezentáció megadása 45 5.53 Nagyobb gyűjtemény jellegű erőforrások lapozhatósága 46 5.6 ÖSSZEKAPCSOLHATÓSÁG 47 5.61 A Link-ek inicializálása 48 5.62 Link relációk 50 5.7 HITELESÍTÉS 51 5.71 Az API kulcsos hitelesítés implementálása 51 5.8 ÁLLAPOTKÓDOK 52 5 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5.81 Globális hibakezelés 52 5.82 A 201 státusz kód 53 5.9 DOKUMENTÁCIÓ 54 5.91 Help Link-ek az erőforrásokban 55 5.10 TESZTELÉS 56 5.101 Egységtesztek 56 5.102 Teszteredmények 57 5.104 Egy kis technológiai affér 59 6. TELEPÍTÉS ÉS
OPTIMALIZÁLÁS 60 6.1 IGÉNY A SZOLGÁLTATÁSA 60 6.11 Kialakítás és kommunikáció 60 6.2 GYORSÍTÓTÁRAZÁS 61 6.21 A átmenetileg tárolható erőforrások meghatározása 62 6.22 A cache-elés beállítása 62 6.23 A hitelesítés 64 6.24 Terhelésteszt 64 7. ÖSSZEFOGLALÁS 66 I. SZ MELLÉKLET 67 IRODALOMJEGYZÉK . 69 NYILATKOZAT . HIBA! A KÖNYVJELZŐ NEM LÉTEZIK 6 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 1. Bevezetés Diplomamunkámmal célom, hogy egy meglévő és működő, Microsoft .NET alapú webes kereskedelmi rendszert kibővítsek web-szolgáltatásokkal, és ezen keresztül bemutassam a RESTful szolgáltatások előállításának módjait Microsoft WCF-ben. Az elkészült termékkel könnyedén össze lehet kötni az alaprendszert más rendszerekkel, áthidalva a platformok közötti differenciákat és a kényes biztonsági kérdéseket. A diplomamunka első fejezeteiben bemutatom a REST alapvető koncepcióját és az
ilyen architektúrájú szolgáltatások előállításának elméleti és gyakorlati módját. Majd ismertetem a WCF érintett szegmensét, csakis a kiszolgálóoldalra koncentrálva. Bemutatom a szóban forgó kereskedelmi rendszer érintett részegységeit, továbbá azt is, hogy milyen apróbb módosításokat kellett alkalmaznom azokon a szolgáltatás kivitelezését megelőzően. Majd részletesen szemléltetem a kivitelezés minden lépését a dolgozat elméleti bevezetőjére építve. A dolgozat utolsó fejezetei a dokumentálást, a tesztelést, a telepítést és az optimalizálást részletezik. Nagy kihívást jelentett egy koros és nagyméretű rendszert kibővíteni modern technológiájú megoldásokkal. Számtalan zsákutcába tévedhetnek a tervezők ilyen esetben, és gyakran ekkor születnek a költséges félmegoldások. Legtöbbször ugyanis újabb eszközökkel fejlesztett, modernebb szemléletű rendszereknél kisebb kockázattal kell számolniuk a
szakembereknek. Dolgozatommal igazolom, hogy régebbi NET-es rendszereket is hatékonyan fel lehet készíteni a jövő igényeire. Az elkészített web-szolgáltatásokat akár más szoftverrendszerek is felhasználhatják, szemléltetni is fogom ennek lehetőségeit. Így a kereskedelmi rendszer az új API segítségével nagy előnyre tesz szert az ERP-k piacán, mivel még napjainkban is alig-alig találkozni jó megoldásokkal. 7 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 2. A REST világ A REST (Representational State Transfer) rendkívül széles körben alkalmazott megoldás. A Google, a Microsoft Azure, az Amazon, a Facebook, a Twitter, a Flicker, a Digg rendszerek rendre rendelkeznek REST API-val. De technológiákat is említhetünk, amelyek használják, vagy épp speciális implementációjuk a REST szolgáltatásoknak: ilyen az RSS, az Atom. Nem is meglepő, hogy a Microsoft vezető technológiái is támogatják a REST szolgáltatásokat; a
WCF, a korábbi ADO.NET Services és a Silverlight A fejezet célja, hogy bemutassam a REST-et és segítsem a ráhangolódást az erőforrás orientál gondolkodásra, a ROA-ra (Resource-Oriented Architecture) [1]. A továbbiakban néhány elemi tény fog felmerülni, amivel már szinte minden ember megismerkedett, aki valaha is használta a webet. A web legfontosabb tulajdonságai, ami miatt olyan gyorsan elterjedt, a következők: • Címezhető erőforrások (URI-kkel) • Standard erőforrás formátumok • Egységes felület (uniform interface) az erőforrások használatához • Állapotnélküliség az ügyfél és a kiszolgáló interakciója között • Hiperhivatkozások (összekapcsolás), melyek segítségével navigálhatunk az erőforrások között Ezek a tulajdonságok teszik oly naggyá a webet. Mégis amikor web-szolgáltatásokról beszélünk, a legtöbb szakembernek a SOAP-alapú szolgáltatások jutnak eszükbe, holott a SOAP egyáltalán nem
a webre született. 2.1 A SOAP A SOAP-ot tipikusan a távoli eljáráshívásra használják. Azt írja le és valósítja meg, hogy két végpont miként kommunikálhat. Továbbá úgy tervezték, hogy protokoll-független legyen Ezért bátran kijelenthetjük, hogy nem feltételül a web az a csatorna, amin a SOAP érvényesülhet, mivel elméletileg nem csak a weben működhetne. Emiatt nem használja ki a web legfontosabb tulajdonságait. Például a SOAP szolgáltatások nem az URI-kre összpontosítanak, inkább a műveleteket helyezik előtérbe. Legtöbbször egyetlen URI-hez lesznek felsorakoztatva az elvégezhető műveletek. Így az 8 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben erőforrások összekapcsolása, mint a web talán legfontosabb tulajdonsága, háttérbe szorul, mivel SOAP szolgáltatásokat kevésbé érdemes hivatkozásokkal összekötni. Szintén érdekes dolog, hogy minden alkalommal, amikor egy SOAP szolgáltatást tervezünk,
egy új interfészt készítünk, és ennek az interfésznek a későbbi módosítása könnyen befolyásolhatja a szolgáltatást felhasználó rendszerek működését. Illetve nem szabad megfeledkeznünk arról sem, hogy a SOAP szolgáltatások tipikusan a HTTP POST műveletét alkalmazzák, melynek hatása sehol nincs definiálva – legalábbis a HTTP-ben nincs –, azaz nem egyértelmű az eredményük. Továbbá a SOAP szolgáltatások nem jól gyorsítótárazhatók, mert legtöbbször nem támogatják a GET műveleteket [2]. 2.2 A REST A REST egy architektúrális stílus, egy tervezési szempont web-szolgáltatások1 készítéséhez, ami 100%-ban a HTTP-re épült. Azaz annak minden tulajdonságát kihasználja, továbbá útmutatót nyújt a REST alapú szolgáltatások előállításához is. A SOAP nem architektúra stílus, semmilyen megszorítást vagy útbaigazítást nem ad, hogy egy szolgáltatás hogyan nézzen ki. Ellenben a REST szolgáltatások a REST
előírásait követik Míg a SOAP szolgáltatások inkább a műveletekre koncentrálnak, a REST szolgáltatások inkább az erőforrásokat veszik alapul, azaz erőforrások útján kommunikálnak a felek. Minden erőforrás egy egyedi URI-vel van beazonosítva. A felhasználó – legyen az egy alkalmazás vagy egy internetes böngésző előtt ülő személy – a HTTP egységes interfészét alkalmazza az URI által beazonosított erőforrás használatakor. Tehát a REST szolgáltatások inkább főnevekkel (pl.: erőforrások) foglalkoznak, míg a SOAP szolgáltatások igékkel (HTTP és SOAP műveletek) [3]. 2.3 A RESTful-ság Azok a web-szolgáltatások, melyek a REST architektúrális stílust követik, RESTful szolgáltatások. Leonard Richardson és Sam Ruby voltak, akik elsőnek összefoglalták a RESTful szolgáltatások lényegét. A későbbiekben rengeteg szakkönyv jelent meg a témakörben, általános témákkal és technológiákhoz specializált témákkal, mint
a Java, Ruby, 1 Továbbiakban, a dolgozatomban a web-szolgáltatás alatt a REST szolgáltatásokat fogom érteni. A SOAP szolgáltatások továbbra is SOAP szolgáltatásokként fogom megnevezni. 9 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben PHP és nem utolsó sorban a .NET A dolgozatom részben ezekből táplálkozik A RESTfulság előírásainak bemutatása könyveket emészt fel, ezért dolgozatomban csak az általam érintett megoldásokon keresztül írom le azokat. A szakirodalomban és a fejlesztők kommunikációjában gyakran találkozni azzal a kijelentéssel, hogy egy bizonyos problémára egy megoldás, nem teljesen vagy teljes egészében RESTful. A dolgozatomban is számos alkalommal teszek ilyen említést. Arra, hogy egy problémára hogyan találok RESTful megoldást, a korábbi tapasztalataim, a szakirodalom és a fejlesztői kommunikációk nyújtanak tudományos segítséget, továbbá a józan és egyszerű gondolkodás. 2.4 REST a
gyakorlatban A REST világgal történő ismerkedéskor érdemes a weboldalakból kiindulni. Bizonyos értelemben egy weboldal is egy REST szolgáltatás. A fejezetben bemutatom, hogy a REST szolgáltatások tervezésekor mely lépéseket kell elvégezni, továbbá kifejtem az alapvető fogalmakat. Szintén kitérek a szigorúbb RESTful szolgáltatások előírásaira is 2.41 Az erőforrások meghatározása Az erőforrás egy olyan valami, ami tárolható egy számítógépen és reprezentálható bitek folyamaként. Azaz lehet egy dokumentum, egy adatbázis egy rekordja vagy épp egy algoritmus eredménye. Továbbá egy erőforrás lehet egy fizikai objektum, mint egy alma, vagy épp egy absztrakt fogalom is, viszont ezen erőforrások megjelenítése kissé problémás lehet. Nagyvonalakban állíthatjuk, hogy vannak dinamikus és statikus erőforrások Erőforrások lehetnek a következők: • Cikk a Silverlight 4-ről • Útvonalterv az iskola és az otthonom között •
Dokumentáció a Microsoft SQL Server 2008 R2-höz • Szoftverfrissítés a Windows 7-hez • Ismerőseim a Facebook-on • A következő öt prímszám 1024 felett • A 223-as rendelés tételei 10 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben REST szolgáltatások tervezésekor az első és legfontosabb lépés az erőforrások meghatározása. Tehát meg kell kérdeznünk magunktól, hogy mik azok a dolgok, amiket közzé szeretnénk tenni. Ezekre először csak összemosódott adathalmazokként tekintünk, majd később ezeket pontosítjuk és átalakítjuk erőforrásokká. [4] [5] 2.42 Az URI-k megtervezése Az erőforrást az teszi erőforrássá, hogy azonosítható URI-vel. Tehát minden erőforrásnak kell lennie URI-jének, legalábbis a weben igen. Fontos, hogy az URI-k leírók legyenek, így még pontosabb lesz a kapcsolat az erőforrás és annak URI-je között. Továbbá ha az URI jól olvasható, értelmezhető akár az ember
számára is, akkor könnyedén lehet vele dolgozni. Az is széppé teszi a munkát, hogy egy erőforrásnak akár több URI-je is lehet, így ha szeretnénk más és más nézőpontból is vizsgálhatjuk ugyanazon erőforrásokat. Lehetséges URI-k: • http://en.wikipediaorg/wiki/Representational State Transfer (REST a wiki-n) • http://www.kereskedelmirendszerhu/Rendeles/223 (A 223-as rendelés) • stb. Az URI-k létrehozása a következő lépés a tervezési folyamatban. Mondhatni ez a munka oroszlán része. A pontos URI design nélkülözhetetlen a tervezési folyamat további lépéseire, illetve a kész szolgáltatások felhasználhatóságára nézve. Itt nevezzük el a korábban meghatározott erőforrásokat. Fontos feltétel, hogy az URI megmagyarázza, hogy pontosan micsodán és miért fog dolgozni a kiszolgáló számítógép. Az erőforrás-URI leképezés legfontosabb szabályai: • Használjunk útvonal változókat (path variable) a hierarchia
leképezéséhez: /parent/child. • Használjunk írásjeleket az útvonal változókban, ahol nem tudunk hierarchiát alkalmazni:/parent/child1;child2. Néhány esetben számíthat az útvonal változók sorrendje. Ekkor többféle írásjelet használunk, de tipikusan a vesszőt és pontos vesszőt. • Használjunk lekérdezési változókat (query variable), mint algoritmus paraméterei, ha algoritmikus erőforrásról beszélünk: /search?q=akarmi&start=20. [6] [7] 11 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 2.43 Az egységes interfész implementálása Mint korábban említettem, egy SOAP szolgáltatás tervezésekor műveleteket hozunk létre. Valójában ekkor egy interfészt tervezünk az alkalmazásunknak. De ez az interfész nem egységes, hiszen teljesen másképp néz ki, mint egy másik cég által fejlesztett rendszer SOAP szolgáltatása. De akár ugyanezen a rendszeren belül egy másik SOAP szolgáltatás is teljesen eltérő
felülettel rendelkezhet. A probléma tehát, hogy az egységesség híján állandóan új interfészek megalkotásán kell gondolkozniuk a SOAP szolgáltatás fejlesztőknek. Másfelől ha egy másik cég fejlesztője szeretné használni a szóban forgó SOAP szolgáltatást, akkor meg kell tanulnia ezt az interfészt, és gyakran azt is, hogy milyen hatásai vannak a benne tartalmazott műveleteknek. A REST-nél szerencsére másképp van. Itt a HTTP által definiált műveleteket – más szóval igéket, vagy verbs-öket – használjuk az URI-k által azonosított erőforrásokon. Ezek a műveletek alkotják az egységes interfészt (1. ábra) [8] GET • Visszaad egy erőforrást • Garantáltan nincs mellékhatása (SAFE) • Gyorsítótárazható POST • Létrehoz egy új erőforrást • Nem biztonságos, a művelet hatása nincs pontosan definiálva a HTTP által (RFC 2616) PUT • Módosít egy létező erőforrást • Ha ismert az URI, akkor erőforrás
létrehozásához használjuk • Akárhányszor hívjuk, mindíg ugyanaz fog történni (idempotens) DELETE • Eltávolít egy erőforrást • Akárhányszor hívjuk, mindíg ugyanaz fog történni (idempotens) 1. ábra Az egységes interfész legfontosabb műveletei Ha REST szolgáltatásokkal dolgozunk, akkor elegendő a fenti egységes interfészt megtanulnunk és alkalmaznunk. Web-szolgáltatás tervezéskor csak azt kell meghatároznunk, hogy egyes erőforrásokon mely műveleteket engedélyezzük, illetve web-szolgáltatások 12 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben felhasználásakor csak azt kell megvizsgálnunk, hogy mely műveletek támogatottak. Természetesen egy erőforrás létrehozásakor nem szükséges az egységes interfész minden verb-jét implementálnunk. Tehát a tervezési folyamat következő lépése az egységes interfész megvalósítása az erőforrásokon. A munkában nem korlátoz a REST, hiszen teljes mértékben a
fejlesztőre van bízva, hogy milyen műveleteket engedélyez. Viszont figyelembe kell venni, hogy a webszolgáltatás a jövőben milyen környezetben fog futni, hiszen elképzelhető, hogy a webkiszolgálók, vagy védelmi vonalak úgy vannak bekonfigurálva, hogy a fenti interfészből csak pár műveletet támogassanak. Tipikusan a PUT és a DELETE áll a problémás kulcsszavak sorában. Kliensoldalon is lehetnek problémák. Korábban a böngészők sem támogatták a PUT és DELETE műveleteket, és ez a böngészőben futó kliensoldali alkalmazásoknál vethetett fel tervezési szintű kérdéseket. Például a Silverlight 2-ben még nem voltak támogatottak a szóban forgó műveletek [9]. A POST műveletet, ha lehetőség nyílik rá, a web-szolgáltatás fejlesztők elkerülik. Azok a REST stílusú web-szolgáltatások, amelyek csupán POST és GET műveleteket alkalmaznak, vagy még rosszabb esetben GET műveletek által módosítanak erőforrásokat, egyáltalán nem
nevezhetők RESTful-nak. RESTful-ok azok a web-szolgáltatások, melyek az egységes interfészt tiszteletben tartva működnek. 2.44 A reprezentáció megtervezése A reprezentáció így definiálható; minden hasznos információ egy erőforrás állapotáról [10]. Mivel egy fizikai objektum nem adat, nem is feltételezhetjük, hogy az a weben elérhető Viszont a meta-információik felhasználhatók az erőforrás reprezentációjaként. Kicsit pontosítva, ha egy erőforrás egy könyv, mint fizikai objektum, akkor annak reprezentációja lehet a meta-információja, például a borítójának egy fényképe, a könyv alapadatai, vagy maga a könyv digitális formában. Visszafele gondolkodva pedig, egy alkalmazásnak elküldhetünk meta-információkat is egy objektumról, amiből az alkalmazás erőforrást állít elő. Pontosan ez történik, amikor kitöltjük egy regisztrációs űrlapon a személyi adatainkat, amiből feltételezhetően egy bejegyzés születik a
regisztrált ügyfeleket tartalmazó adatbázis táblában. Illetve meglévő erőforrásnak is készülhet új reprezentációja, például amikor egy fényképet küldünk el egy alkalmazásnak és az átkonvertálja, átméretezi, vízjellel látja el azt. 13 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Az erőforrások megjelenése rendkívül változatos. Korábban a web-en média típusoknak (media types, HTTP fejlécekben Content-type) nevezték a szakemberek az erőforrások reprezentációit. Ha egy weboldalt tekintünk erőforrásnak, akkor a reprezentáció HTML vagy XHTML. Üzleti rendszerekben legtöbbször XML vagy manapság divatos JSON (JavaScript Object Notation) a használt. Továbbá a hírforrásoknál az RSS és az Atom a leggyakoribb Természetesen egyéb erőforrások, mint például az audio-video erőforrások, képek, ikonok a saját, legtöbbször formátum-specifikus reprezentációval rendelkeznek. De a reprezentáció nem csak
média típusokra terjed ki. Egy szöveges dokumentum reprezentációja lehet a nyelvi kultúra, amiben olvasható. Vagy épp egy termék ára, mely értékének szöveges formátuma illetve pénzneme is a reprezentációtól függhet. A REST lehetőséget ad arra, hogy egy erőforrásnak több reprezentációja is lehessen. Célszerű a reprezentációt az erőforrás URI-jében alkalmazni: • http://www.kereskedelmirendszerhu/Rendeles/223xml (A 223-as rendelés XML alakban) • http://www.kereskedelmirendszerhu/Rendeles/223json (A 223-as rendelés JSON alakban) • http://www.kereskedelmirendszerhu/Rendeles/223xhtml (A 223-as rendelés XHTML alakban) A nyelvi kultúrák esetén például: • http://www.documentscom/hu/documents/101 (A 101-es dokumentum magyarul) • http://www.documentscom/en/documents/101 (A 101-es dokumentum angolul) A tervezési folyamat újabb és legtöbb esetben befejező lépése az erőforrásaink reprezentációinak meghatározása. Teljes
mértékben szabad keze van a fejlesztőknek a formátumok kiválasztásában. Viszont egy erőforrás elérésekor a kívánt reprezentáció megadása több féle módon is történhet. A leginkább javasolt megoldás, hogy ami csak lehet, kerüljön bele az URL-be. Ellenben használhatók a HTTP kérés fejlécek is Ez utóbbi hasznos lehet a kívánt nyelvi kultúra meghatározáskor, a böngészők az Accept-Language fejlécet használják erre. Mindkét megoldás RESTful [11] Figyelembe kell venni, hogy a web-szolgáltatást gyakran más rendszerek fogják felhasználni. Így a megfelelő reprezentáció létkérdés Továbbá kényes probléma, ha a reprezentáció megváltozik, miközben a szolgáltatást más rendszerek használják. Gyakorlati példa, ha egy rendelés XML reprezentációja szerkezetileg átalakul, és a kliens rendszerek erről nem értesülnek. 14 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 2.45 Kapcsolatok kialakítása A web
talán legfontosabb tulajdonsága a linkelhetőség. Azaz hyperlink-ek segítségével kapcsolatok létesítése az erőforrások között. Egyik erőforrásról a másikra navigálhatunk, anélkül, hogy gondolkoznunk kellene a kívánt erőforrás URI-jén. Továbbá így lehet az állapot nélküliségnek állapotot adni. Gondolva például egy találati lista lapozhatóságára, ahol az oldalszámok hiperhivatkozások, így tudjuk, hogy hányadik oldalon vagyunk. Egy üzleti rendszerben rendkívül előnyös, ha egy XML reprezentációjú rendelési tételből le tudja tölteni a kliensalkalmazás a megrendelt terméket, esetleg a megrendelő adatait, mint erőforrásokat. Ehhez az kell, hogy a link-eknek megfelelő formában az erőforrásban kell szerepelniük. A tervezési folyamat utolsó lépése az erőforrások közötti kapcsolatok felépítése linkek segítségével. Ez nem egyszerű dolog, mivel a link-ek reprezentációfüggők Egy JSON formátumú erőforrásban nem
néz ki szépen XML formátumú erőforrásra mutató link. Vagy ha mégis megengedünk ilyet, akkor tájékoztatni kell a klienseket arról, hogy milyen reprezentációjú erőforrásra mutat egy-egy link. Továbbá a kliensalkalmazások gyakran rákeresnek az erőforrásokban a linkekre, így gondoskodni kell a link-ek egységes megjelenéséről. Vannak erőforrások, amikbe nem lehet linkeket helyezni, tipikusan ilyenek a bináris adatok. Ekkor válaszfejlécek használata javasolt [12] 2.5 Tervezési minták, speciális megoldások Az évek során a tervezési folyamat minden lépéséhez kialakultak frappáns tervezési minták, melyeket célszerű ismernie a fejlesztőnek, ha RESTful szolgáltatásokat készít. Dolgozatomban is néhány alkalommal alkalmazok ilyen trükköket. 2.51 Tervezési minták erőforrásokhoz Léteznek tervezési mintákból eredő erőforrás típusok. Ilyenek például a kompozit (composites) erőforrások, melyek több erőforrás együttesét
jelentik. Legyen egy weblap a 15 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben kompozit erőforrás, ahol legtöbbször több kisebb erőforrás szerepel egy helyen. Mint amikor egy ügyfél adatlapján meg tudjuk tekinteni annak kapcsolattartóit, korábbi megrendeléseit. Ezzel a megoldással gyorsíthatjuk az adatátvitelt, hiszen nem kell minden szükséges erőforrást egyenként lekérdeznünk [13]. A vezérlő erőforrások (controller resources) hosszabb, összetettebb szerveroldali műveletekhez használjuk, melyek legtöbbször több erőforrást is manipulálnak. Ilyen megoldást használnak, amikor egy telefonban lévő partnerlistát szinkronizálnak a szerveren lévő partnerekkel. Nem kell le- majd feltöltögetni a szerveren lévő partnereket, továbbá szükségtelen üzleti logikát tervezni a szinkronizáláshoz a kliensen. Egyszerűen feltöltjük a telefonban lévő partnerlistát a szerverre, mint controller erőforrás és a szerver
elvégzi a dolgát. Ezzel nagyobb teljesítményre teszünk szert, kevésbé terheljük a hálózatot, nem kell szinkronizáló üzleti logikát létrehozniuk a kliensek fejlesztőinek és talán eredményt is kapunk a szinkronizálás hatásairól [14]. A tranzakciók is támogatottak a HTTP-n. Például, amikor egy banki tranzakcióról van szó, ahol az egyik számláról kell egy másikra utalni. A tranzakciót kezelhetjük erőforrásként, mint „banki átutalás tranzakció”, létrehozhatjuk (POST, PUT), vagy akár törölhetjük (DELETE). Egy hosszabb lefolyású tranzakciót, mint egy helyfoglalást a repülőn szintén így kezelhetünk [15]. 16 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 3. A WCF RESTful programozási modellje A WCF kliensoldalon és szerveroldalon is segíti a fejlesztők munkáját, szolgáltatások egyszerű létrehozásában és azok – vagy épp más rendszerek szolgáltatásainak – felhasználásában is. A SOAP
szolgáltatások mellett a REST szolgáltatások létrehozását is bőkezűen támogatja. A fejezetben a WCF web programozási modellre koncentrálok, melyet legtöbbször WebHttp-nek, WebHttp Services-nek neveznek. Továbbá csak a szerveroldali viselkedést mutatom be. 3.1 WCF a szerveroldalon A WCF feladata szerveroldalon, hogy lefülelje a hálózaton át érkező üzeneteket és azokat feldolgozva továbbítsa a szolgáltatás kódjához, a fejlesztő implementációjához. Illetve üzenetet küldjön ki a hálózatra. Hálózat Üzenet Diszpécser 2. ábra A WCF szerveroldali csatorna verme Amikor megnyílik egy WCF szerveroldali végpont, a WCF csatornafigyelő (channel listener) segítségével létrehoz egy hálózat figyelőt, un. átviteli csatornát A csatornafigyelő valójában egy gyár objektum, ami azért felelős, hogy előállítsa a szerveroldali figyelő infrastruktúrát. 17 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Az
üzenetértelmező a hálózati üzenetet átalakítja a WCF számára értelmezhető üzenetté. Ez az üzenet System.ServiceModelChannelsMessage típusú, melyből komplex NET objektumokat is vissza lehet állítani. A protokoll csatornák opcionálisak a modellben. Ezek segítségével lehet bizonyos stílusú architektúrákat implementálni. Fontosak lehetnek továbbá a nagy biztonságú és nagy megbízhatóságú szolgáltatások készítésekor. A diszpécser a beérkező üzenetek számára megfelelő metódusokat hívja meg. Ezek a metódusok már a fejlesztők kódját tartalmazzák. Először megvizsgálja, hogy melyik metódus a megfelelő, majd az üzenetet visszaállítja a kívánt .NET típussá, és meghívja a metódust – a szolgáltatást. Ezek együttesen alkotják a csatorna vermet, mely létrehozásához a WCF kötéseket (bindings) használ. A kötések konfiguráció beállítások, melyek konfigurációs file-ban vagy a memóriában, objektumként is
elhelyezkedhetnek. Leggyakrabban a konfigurációs file-ban tartjuk nyílván ezeket, de .NET 4 óta már erre sincs szükség, deklaratív programozásra is van lehetőség. Természetesen manuális kódolással is elő lehet állítani a csatorna vermet, a fenti komponensekkel. Két generációval ezelőtt a WCF 30-ban csak erre volt lehetőség [16] 3.2 A WebHttp szolgáltatások A System.ServiceModelWebdll szerelvény a 35-ös NET óta segíti a fejlesztőket Ez tartalmazza a WebHttp, és így a REST szolgáltatások létrehozásához szükséges komponenseket. A SystemServiceModel és a SystemServiceModelWeb névterek a lényegesek. Néhány fontosabb komponens: • WebHttpBinding – ez a kötés a HTTP(S) átviteli csatornát és az üzenet értelmezőt (TextMessageEncoder) állítja be. • WebHttpBehavior – ez állítja be a WCF diszpécser rétegét, hogy a REST kéréseket a megfelelő szolgáltatás CLR metódusához irányítsa – routing. Továbbá ez konfigurálja
be a sorosító/visszaállító réteget. • WebOperationContext – ennek a környezeti komponensnek a WebOperationContext típusú, osztályszintű Current nevű tulajdonságával lehet elérni a kérelmek és válaszok minden részletét. Ez rendkívül fontos objektum, például ha HTTP kérésekből szeretnénk adatokat kinyerni, mint a HTTP fejlécek. 18 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben • System.UriTemplate – ezzel a komponenssel lehet megmagyarázni a WCF-nek, hogy egy kérelem URI-je számára mely metódust kell meghívni. Minden szolgáltatás metódusnak egy URI mintája lesz. A WCF összehasonlítja a kérelem URI-jét a UriTemplate-ekben definiált URI mintákkal, és úgy hoz döntést. Ez a komponens, és ennek alapos ismerete a REST szolgáltatások URI tervezésénél nagyon fontos. • WebGetAttribute és WebInvokeAttribute attribútumok – deklaratív programozással lehet megadni, hogy mely metódus legyen
meghívva egy bizonyos HTTP kérés esetén. A WebInvoke rendelkezik egy string UriTemplate tulajdonsággal, ahol az URI mintát lehet meghatározni, továbbá egy string Method tulajdonsággal, ahol a HTTP igét lehet megadni, amelyre a metódusnak válaszolnia kell. A WebGet annyiban speciális, hogy Method tulajdonsága alapértelmezetten GET igére van beállítva. Mindkét attribútumnál meg lehet határozni explicit módon a kérés és válasz formátumát. Tehát ezek az attribútumok használatosak az URI tervezéskor, a REST egységes interfész megvalósításakor, továbbá a reprezentáció kialakításakor is. Mivel a WebHttp szolgáltatások deklaratívan, attribútum alapú programozással és részben konfigurációval lesznek definiálva, gyakran futási időben derül ki, hogy a szolgáltatások működnek-e. Például a leggyakoribb problémák a UriTemplate-ek hibás megadása, vagy azok ismétlődése. Ezek futásidejű hibát váltanak ki A WCF REST
szolgáltatások reprezentációjánál alapértelmezetten az XML és a JSON sorosítás támogatott. Ebben a Data Contract sorosítási megoldás nyújt segítséget Természetesen a Message objektummal tetszőleges reprezentáció implementálható. Dolgozatomban a Data Cotract sorosítást fogom alkalmazni. [17] 3.21 A WCF WebHttp szolgáltatások adottságai fejlesztői szempontból A szolgáltatások súgó oldalait a korábbi WCF verziókkal szemben már nem kell manuálisan elkészíteni, hanem dinamikusan reflexióval, futási időben előállítódnak. Csupán a szolgáltatásokon /help erőforrást kell lekérdezni. A Help oldalakon nem csak az URI template-ek, de még a támogatott HTTP igék, a kérések és válaszok reprezentációja is 19 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben legenerálódik. Az utóbbi XML esetén részletes és érvényes XML sémával, és minta adattal JSON esetén minta adattal [18]. Content-Type HTTP fejléccel
lehet megadni a kérésekben, hogy milyen reprezentációra van szükségünk. Deklaratív módon tudjuk megadni a szolgáltatások metódusainál a gyorsítótárazási beállításokat. Ügyesen lehet kezelni a konkurenciát és olyan kivétel típusokat állnak rendelkezésünkre, melyekbe bármilyen sorosítható objektumot be lehet tenni és a válasz üzenetben JSON-ban vagy XML-ben meg lehet azt jeleníteni [19]. A Visual Studio template-ekben pedig megtalálható az API kulcsos hitelesítés, amit a dolgozatomban is használni fogok. 3.3 A Data Contract sorosítás A WCF REST programozás a WCF 3 óta lehetőséget ad a Data Contract sorosítás használatára. Ennek segítségével lehet megmagyarázni a NET-nek, hogy egy objektumot hogyan generáljon le XML-be, JSON-be vagy épp egyedi formázókkal tetszőleges formátumba. Rendkívül egyszerű használni, verzió toleráns és lehetőséget ad arra, hogy a kimenetben megőrizzük az objektumhivatkozásokat. A Data
Contract sorosító a típusokban lévő tulajdonságokra koncentrál, mezőket – legyenek azok privát vagy publikusak – ugyan lehet, de nem célszerű vele sorosítani. Minden primitív típust feldolgoz, referencia típusokat, érték típusokat és ezek Nullable változatait is, de még akár az IEnumerable-eket is. byte[]-ot Base64-ben sorosít XML formázás esetén, a System.IOStream típusú folyamok változatlanul kerülnek le az ügyfélhez Figyelembe veszi a SerializableAttribute-tal ellátott vagy ISerializable-t implementáló típusokat. Továbbá minden olyan típust, ami „ismertté” lett téve a sorosító számára [20]. A WCF-el történő fejlesztés – főleg a HTTP-n – könnyedén elcsúszhat időben és így költségekben is, ha fejlesztő nem ismeri a Data Contract sorosítást és annak sajátosságait. Dolgozatomban is a Data Contract-ra támaszkodom. A munkám során a következőkben leírt szabályok szerint jártam el, amikor a reprezentációt
kiviteleztem. 20 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 3.31 Deklaratív programozás Alapból minden osztály példányát le tud sorosítani a Data Contract sorosító WCF REST szolgáltatásoknál. Nem szükséges a típusokat kidekorálni De ha némiképp bele szeretnénk szólni a formátumba, akkor célszerű a Data Contract attribútumokat alkalmazni. Ezeket a System.RuntimeSerialization névtérben és ugyanezen elnevezésű szerelvényben lehet megtalálni. • DataContractAttribute – Azokat a típusokat, amiket le szeretnénk sorosítani, vagy épp részt vesznek a sorosítandó objektum gráfban, meg kell jelölni ezzel az attribútummal. Az attribútum tulajdonságaival explicit módon lehet definiálni a példány nevét a kimenetben, ami alapból a típus neve. Továbbá XML kimenet esetén az XML névteret. A dolgozatomban nem használok XML névtereket, tehát a string DataContractAttribute.Namespace tulajdonságot üres string-gel
helyettesítem • DataMemberAttribute – Ezzel az attribútummal kell ellátni azokat a tulajdonságokat, amelyek értékeit le szeretnénk sorosítani. Itt szintén meg lehet határozni a tulajdonság nevét a kimenetben, ami alapból a kódbeli tulajdonság név. Továbbá megadható, hogy milyen sorrendben kerüljenek feldolgozás alá a tulajdonságok, ami rendkívül fontos dolog, mert a Data Contract sorosító érzékeny a tulajdonságok és mezők sorrendjére. Továbbá meg lehet adni, hogy egy tulajdonság kötelező-e vagy sem. Ha bool DataMemberAttribute.IsRequired igaz, és nincs jelen a mező értéke visszaállításkor az adatfolyamban, akkor kivételt kapunk. • IgnoreDataMemberAttribute – Ha egy mezőt, vagy tulajdonságot ki szeretnénk hagyni a feldolgozásból, akkor ezzel az attribútummal kell megjelölni. • CollectionDataContractAttribute – Ha objektumok gyűjteményét akarjuk lesorosítani és némiképp bele szeretnénk szólni a gyűjtemény
formátumába, akkor ezzel az attribútummal egyedileg létrehozott gyűjtemény típusokat tudunk kidekorálni. Itt is meg lehet határozni a gyűjtemény nevét a kimenetben, továbbá az elemek nevét is még szótár típusú gyűjtemény esetén is. Hasonlóan a DataContractAttribute-hoz, itt is üres string-gel helyettesítem a Namespace tulajdonságot. [21] 21 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 3.32 Öröklés és interfészek implementációi Örökléskor a leszármazott típusok sorosítása teljesen automatikus. Annyi csupán a feltétel, hogy leszármazottak meg legyenek jelölve a DataContract-tal. Ha polimorfizmus van jelen, netán interfész típusú tulajdonságokkal rendelkezik egy sorosítandó osztály, ahol kései kötés érvényesül, akkor tudtára kell adni a sorosítónak, hogy milyen „váratlan” típusokkal találkozhat a feldolgozás során. Ehhez a KnownTypeAttribute attribútumot kell alkalmazni a szóban forgó
osztályon2. Ez biztonsági okokból alakult így ki a Data Contract sorosítóban [22]. 3.33 Hivatkozások és körkörös hivatkozások Ha egy objektumra két referencia is hivatkozik, akkor alapból a Data Contract kétszer sorosítja le a hivatkozott példányt. Visszaállításkor így két egymástól független objektum épül fel a memóriában. Ez problémát jelent a ciklikus hivatkozásoknál, ami támadási pont lehet a webes rendszerek számára – például DOS [23]. Lehetőség van arra is, hogy a kimenetbe csak egyszer kerüljön be az objektum, és hivatkozások legyenek elhelyezve, de a dolgozatomban az előző, alapértelmezett beállítást használom, mivel az alaprendszerben körkörös hivatkozások nincsenek. 3.34 Verzió tolerancia A korábbi formázóknál, mint például a bináris vagy SOAP formázó, problémát okozott a verzionálás. Ha egy osztály kibővült egy új tulajdonsággal, akkor a régi adatfolyamból nem lehetett visszaállítást
végezni az új típusba. De ha több adat volt az adatfolyamban, mint amennyit várt a visszaállítandó típus, akkor is kivételt kaphattunk [24] [25]. A Data Contract szérializáló átugorja azokat az adatokat, amelyeknek nincs DataContract-tal megjelölt megfelelőjük a típusban. Továbbá nem csinál ügyet abból sem, ha egy DataContract-os mező értéke nincs benne a folyamban. Mint ahogy az XML sorosítónál megszokhattuk, a Data Contract is összegyűjti a visszaállítás során talált ismeretlen adatokat, amiket rendelkezésére 2 Mint majd később említem, az alap rendszerben a komponensek pontos interfész hierarchiára épülnek. Így a tulajdonságok interfész típusúak. Ezért nagyon sok esetben rákényszerültem, hogy az osztályokon a DataContract attribútum mellett KnownType-ot is használjak. Így már a Data Contract sorosító fel volt készítve a „váratlan” típusokra. 22 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben
állít a programozónak a művelet befejeztével. Ezeket az adatokat egy fekete dobozba gyűjti és az IExtensibleDataObject implementálásával elérhetjük [26]. 3.35 Null és üres értékek A fejlesztői munkám során több alkalommal is találkoztam olyan REST (de nem feltétlenül RESTful) szolgáltatásokkal, ahol nehézséget okozott az az eset, amikor egy XML reprezentációban bizonyos elemek vagy attribútumok nem jelentek meg, ha azoknak nem volt értékük a kért erőforrásban. Vagy éppenséggel az elemek jelen voltak ugyan, csak értékkel nem rendelkeztek. A legbosszantóbb, ha az egész web-szolgáltatáson belül, vagy épp csak egyetlen erőforráson belül ez a viselkedés vegyes. Például ha vannak mezők, amik null érték esetén nem jelennek meg, és mellettük vannak olyanok is, amelyek megjelennek ugyan, de érték nélküliek, vagy legrosszabb esetben valami buta alapértékük van. Sokkal szerencsésebb, ha nil=”true” - hoz hasonló
attribútummal látják el azt. Nem csak akkor okoz problémát ez az eset, ha erőforrást kérdezünk le, hanem legfőképp akkor, amikor egy erőforrást módosítunk. Hiszen nehéz a kliensoldali programozó számára kitalálni, hogy az erőforrás egyik mezőjét hogyan módosítsa null-ra. Ez rengeteg kísérletezést és kódolási munkát ró a szolgáltatást felhasználó programozó vállára. Ennek elkerülése érdekében a web-szolgáltatásom minden esetben az XML-be írja az érték nélküli elemet, illetve nil=”true” attribútummal látja el azt. Továbbá erőforrás módosításakor is minden elemet célszerű elküldenie a kliensoldali alkalmazásnak. Mindez köszönhető a Data Contract sorosítónak, mivel ott ez az alapértelmezett viselkedés. Természetesen a sorosítónak meg lehet mondani, hogy ha egy tulajdonság null értékű, akkor ne kerüljön be az elem az adatfolyamba: bool DataMemberAttribute.EmitDefaultValue=false [27] 23 Pótári Tamás
RESTful web-szolgáltatások készítése WCF-ben 3.36 Gyűjtemények A Data Contract sorosító gond nélkül lesorosítja a gyűjtemény típusú tulajdonságokat. A gyökér elem a tulajdonság neve lesz és az elemek pedig a gyűjtemény tagjainak sorosított példányai. Ha polimorfizmus érvényesül, tehát például egy IList<T> típusú a tulajdonság, akkor a polimorf gyűjtemény kerül sorosításra. Fontos, hogy ilyenkor a sorosító semmilyen információt nem tárol el a kimenetben a lesorosított gyűjteményről, ezért visszaállításkor a rendszer nem fogja tudni, hogy milyen típusú objektumot tegyen az IList<T> típusú referencia helyére – a sorosító ilyenkor egy egyszerű tömbbel próbálkozik. Sikertelen próbálkozás esetén a feldolgozó kihagyja ezt a tulajdonságot, ezért erre különösképp oda kell figyelni [28]. A dolgozatomban saját gyűjteménytípusokat fogok használni, melyek zárt generikus típusból származnak és a
CollectionDataContractAttribute-tal lesznek ellátva. 3.37 Néhány sajátosság Sok kezdő fejlesztő abba a hibába esik, hogy azt feltételezi, a Data Contract nem érzékeny a tulajdonságok vagy mezők sorrendjére, pedig nagyon is. Egy olyan XML elemet, amely nem abban a pozícióban helyezkedik el, ahogy alapból a típusban meg van határozva, kihagyja a feldolgozásból. A tulajdonságok vagy mezők feldolgozása a következők szerint zajlik: 1. Ősosztálytól a leszármazottig 2. Ha van beállítva DataMemberOrder, akkor a kisebb értéktől a nagyobb értékű mezőig 3. A mezőnevek ABC sorrendje szerint [29] Dolgozatomban a komponenseimben nem határoztam meg explicit módon a DataMember.Order-t, csupán a mezőnevekre hagyatkoztam Továbbá a Data Contract sorosító, XML esetén csak elemeket hoz létre, attribútumokat nem. Így egyedi attribútumok értékeit nem veszi figyelembe az XML dokumentumban visszaállításakor. Ezeket a sajátosságokat rendkívül
fontos közölnöm a web-szolgáltatásom felhasználóival, hiszen ha nem figyelnek a XML elemek sorrendjére, akkor akár adatvesztést is előidézhetnek. Továbbá van egy korlátozás a WCF Data Contract sorosítójában; alapértelmezetten csak 65536 objektum kerül sorosításra, és ha ezt a határt eléri, akkor kivétellel megszakad a 24 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben végrehajtás. Ezt az értéket célszerű módosítani az alkalmazás konfigurációjában A szolgáltatásomban is így járok el (3. ábra) <system.serviceModel> <behaviors> <serviceBehaviors> <behavior> <dataContractSerializer maxItemsInObjectGraph="200000" /> 3. ábra A sorosító által lesorosítható objektumok száma 200000-es értékkel 25 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 4. Az alaprendszer ismertetése Az alaprendszer egy vállalati információs rendszer, melynek fejlesztése
2008-ban indult, C# nyelven. Akkor még Microsoft NET 20 volt aktuális, majd a projekt az evolúcióval minden .NET verzióra migrálva lett, az akkori legaktuálisabb Microsoft Visual Studio-val Jelenleg a 4.0-ás keretrendszerrel működik Főbb szolgáltatásai a professzionális termékkezelés, ügyfél és kapcsolattartó nyilvántartás, árajánlatok, rendelések, elosztási tervek, számlázás, raktárműveletek, logisztika, folyamatirányítás, reklamációkezelés és pénzügyek. A rendszer főbb rétegei: • A megjelenítési réteg klasszikus ASP.NET Web-alkalmazás, mely 40-ás NET-en működik. Közel 400 gazdag funkcionalitású, több nyelvre lefordított webformból és majd 100 egyedileg tervezett vezérlő elemből áll jelenleg. • Az üzleti logikai réteg a komponens-orientált szoftverfejlesztés szabályait szem előtt tartva született meg, melyből profitáltam a web-szolgáltatás fejlesztés során. Jelenleg 24 Visual Studio projekt, azaz 24
szerelvény alkotja ezt a réteget. A projektek pontos tervezés útján, a lefedett témakörök3 csoportosítása alapján jöttek létre. A komponensek POCO (Plain Old CLR Object) osztályok. • Az adatrétegért SQL Server 2008 R2 felelős. Egy adatbázisban jelenleg körülbelül 170 tábla és 900 tárolt eljárás és még megannyi trigger, UDF és .NET-ben implementált UDA található. A rendszer példányai jelenleg Windows Server 2008 R2-n, IIS 7.5 kiszolgáló alatt futnak az USA-ban elhelyezett szervereken. A kereskedelmi rendszerből csupán egyetlen témakör csoport, a terméktörzs lesz felhasználva, melynek elnevezése implementációja a Products. Products Az szerelvényben található. A 4 ábra szemlélteti, hogy a Products 4. ábra A Products szerelvény és függőségei szerelvény mely más szerelvényektől függ. A web-szolgáltatások szintén használni fogják ezeket a szerelvényeket, és némi módosításon is át kell esniük a
kivitelezés során. 3 A dolgozatomban témakörnek nevezem az elemi entitásokat, mint például a Termék, Gyártó. Témakör csoportnak pedig az ezeket összefoglaló fogalmat, mint például Terméktörzs. Így a Termék témakör rendszerbeli megfelelője a Product komponens, az ezt tartalmazó témakörcsoport pedig maga a Products szerelvény és Products névtér. 26 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 4.1 Az érintett szerelvények ismertetése A Security szerelvény a biztonsági megoldásokért felelős. Biztonsági szerepkör és tagság providerek implementációit tartalmazza, továbbá itt vannak nyilvántartva a rendszerben használt biztonsági szerepkörök. A fő névtere a Security A rendszer összes szerelvénye függ ettől. A CommonTypes a rendszer számára nélkülözhetetlen komponenseket, típusokat tartalmazza: interfészek melyeket a legtöbb komponens közvetlenül vagy közvetve implementál, speciális kivétel
típusok (kivétel hierarchiák) és speciális attribútum típusok. Ez a szerelvény biztosítja a több nyelvi kultúrában történő munkát is. Fő névtere a CommonTypes, és hasonlóan a Security-hez, ez a szerelvény is szinte minden szerelvény által hivatkozott. Két fontos interfésze az ISavingServices és IDeletingServices (5 ábra) Azok a komponensek, melyek implementálják ezeket az interfészeket, elmenthetők és törölhetők. Az adatbázisba mentéskor az bool ISavingServices.IsNew tulajdonság határozza meg, hogy meglévő objektum módosítása, vagy új objektum létrehozása történjen. Továbbá az objektumokkal történő munka során útmutatót ad attól, hogy már eltárolt objektumokkal dolgozunk-e. 5. ábra A menthető és törölhető komponensek interfészei A Currency a rendszerben tárolt pénzértékek kezeléséért felelős. A pénzértékek nem decimálisként, hanem xml-ként kerülnek tárolásra az adatbázisban, Data Contract4 sorosítás
útján. Így egy érték minden – a rendszer által támogatott – devizanembe átváltva kerül mentésre, az akkori árfolyam szerint. A Price és Prices komponensek a típusos megfelelői az áraknak (6. ábra) Természetesen vannak árak, amelyek futási időben lesznek kikalkulálva, 4 Mivel Data Contract sorosítás lett alkalmazva, ezért nem csak XML-be, hanem akár JSON formátumba is lehet sorosítani a típus példányait. Ez pont megfelelő a web-szolgáltatásom számára 27 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben aktuális árfolyam szerint. A szerelvény megfelelő komponense átváltásokat is végez webszolgáltatások segítségével Ez a funkcionalitás az ICurrencyRateServices interfész segítségével kiterjeszthető, így akár több átváltó web-szolgáltatást is tud használni. A fő névtere a Currency és majdnem minden szerelvény hivatkozik rá. 6. ábra A pénzértékek kezelését segítő típusok A Products áll
a web-szolgáltatás középpontjában. A legtöbb kereskedelmi rendszerben található terméktörzs, mely termékeket, gyártókat és egyéb témaköröket tartalmaz. Itt a Products rendkívül izmos, témakörcsoportja tág, rengeteg komponensből áll. A termékképek, vonalkód kezelés és előállítás, termékcímke nyomtatás, keresztreferencia cikkszámok professzionális támogatásán át a dinamikus termékosztályozásig. Továbbá a kötegelt, Open XML alapú termékadat módosításra is tartalmaz implementációt. A dolgozat, csak néhány témakört érint. Főbb névtere a Products 28 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 4.2 A rendszer néhány fontos tulajdonsága A rendszer a komponens-orientált szoftverfejlesztés szabályai szerint épült. A cél az volt, hogy a komponensek újrahasznosíthatóak legyenek bármilyen rendszerben, bármilyen .NETes programozási nyelvben 4.21 Interfészek és implementációik Mint ahogy a
7. ábra mutatja, a komponensek interfészeket implementálnak, melyek elnevezése rendre azonos a komponens (a témakör) nevével. 7. ábra A termék és gyártó komponens A rendszerben minden komponens példánya csakis interfészeken keresztül van elérve és használva. Így lehetőség nyílik arra, hogy egy termék objektumra bizonyos esetben másként tekintsünk, mint amit a komponens definíciója leír, továbbá a kliens kód kevésbé lesz érzékeny az implementáció megváltozására [30]. Jelenleg az interfészek és azok implementációi azonos szerelvényekben találhatók. 29 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben A 8. ábra ábrázolja a gyártó és termék interfészek hierarchiáját Az interfészekkel némi probléma lesz a Data Contract sorosításnál, erre a dolgozatomban később részletesebben visszatérek. 8. ábra A termék és gyártó interfészek 4.22 Komponens alapú biztonság Az alaprendszer tervezésekor a
komponens alapú biztonság alapvető fontosságú volt. Szerepkör alapú biztonság érvényesül, amely pontosan definiált biztonsági szerepkörökkel van alátámasztva. A kódot végrehajtó felhasználó csakis azokat a szolgáltatásokat veheti igénybe, melyeket a biztonsági szerepkörei elérhetővé tesznek [31]. A 9 ábra jelzi, hogy egy Products.ProductSave() metódust csakis olyan felhasználó hívhat, aki termékkarbantartó adminisztrátor vagy termékkarbantartó raktáros. 9. ábra Komponens alapú biztonság implementációja 30 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Megeshet, hogy bizonyos típusokat még csak használni, sőt még reflektálni se tud egy olyan kliens kód, akinek a felhasználója arra jogosulatlan. A komponens alapú biztonság ilyen módú implementációja rengeteg munkát takarított meg a web-szolgáltatás implementálásakor. 4.23 A lokalizáció A komponensek futási időben ”lefülelik” a
végrehajtó szál nyelvi kultúráját. Ennek megfelelően például egy termék alapadatai olyan nyelven kerülnek a Products.Product objektum megfelelő tulajdonságaiba, amilyen nyelven a felhasználó szeretné – már ha az a nyelv támogatott és van is adat eltárolva hozzá az adatbázisban. Továbbá, ha egy kivétel keletkezik, akkor a hibaüzenetek is a megfelelő nyelven5 jelennek meg. A hibaüzenetek resxekben, típusos erőforrás komponensekben vannak nyilvántartva A kivételek pedig pontosan definiált, egyedileg tervezett kivétel-hierarchiából kerülnek ki, melyek speciális egyedi attribútumokkal vannak osztályokba sorolva (deklaratív programozás). 4.24 Példányosítás, mentés, törlés és listázás A komponensek példányosítása paraméteres vagy paraméter nélküli konstruktorokkal történik, a törlésük és mentésük a korábban már ismertetett CommonTypes.ISavingServices és CommonTypes.IDeletingServices interfészek szerint mehet
végbe. A komponensek tulajdonságainak set accessor-ai ellenőrzöttek, tehát már értékadáskor ellenőrzik, hogy az értékül adott érték megfelelő-e az objektumnak. Ha nem, akkor azonnal kivétel keletkezik, lokalizált hibaüzenettel. Ugyanilyen ellenőrzés mentéskor is történik, így garantált, hogy az adatbázisba már csak érvényes adatok kerülnek be. Értelemszerűen az adatbázisban is implementálva vannak szigorú biztonsági megszorítások, ellenőrzések, ha esetleg nem csak ezek a komponensek használnák az adatbázist. A listázás – például gyártók szűrőfeltétel szerinti listázása – gyűjteménnyel visszatérő statikus metódusokkal valósul meg, melyek rendre a megfelelő komponensekben találhatók meg. A visszaadott gyűjtemények legtöbbször DataSet-ek, melyek az alaprendszer ASPNET- 5 A diplomamunkám idejében az angol és magyar nyelvi kultúra volt támogatva, 60%-ban pedig a német. 31 Pótári Tamás RESTful
web-szolgáltatások készítése WCF-ben es klienskódjánál rendkívül produktívak6, továbbá típusos listák, melyek más üzleti logikáknál és majd a web-szolgáltatásnál válnak hasznossá. 10. ábra Gyártók listájának kinyerése A 10. ábra jelzi, hogy System.CollectionsGenericIList<T>-ként – nem is ki típusos lehet – nyerni DataSet-ként gyártókat, ahol és T Products.IManufacturer kell, hogy legyen Tehát a rendszer ekkori állapota szerint Products.IManufacturer, ProductsManufacturer vagy ezek leszármazottjai A Terméktörzs témakörcsoport két fontos biztonsági szerepköre is látható a fenti ábrán: a termékkarbantartó, és a termékolvasó. Az előbbivel rendelkező user nem csak olvashatja, de módosíthatja is a témakörcsoport témaköreit. 6 A DataSet-ek közkedveltek voltak az ASP.NET programozók számára, mivel a vezérlőelemek könnyen tudtak belőlük táplálkozni, továbbá a sorok rendezése is
implementálva van bennük. A rendszerben a DataSet-et csakis adat kiolvasásra szolgálnak, adat módosításra nem. 32 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5. Kivitelezés A kivitelezés során készült el a web-szolgáltatás. A WCF-el és kellő szakértelemmel nagyon egyszerűen, mindössze pár napos munkával elő lehetett állítani egy használható szolgáltatást. A tesztelés, dokumentálás, beüzemelés és esetlegesen az optimalizálás már több időt vett igénybe. A valóságban munka során először egy read-only szolgáltatás készült el, ami csak az erőforrások olvasására jó, és ezt követően implementáltam az erőforrások módosításait. Az ok az volt, hogy a készülő web-szolgáltatásra egy külső vállalkozás máris igény tartott, és hogy támogassam a tervezési munkájukat, rendelkezésükre állítottam ezt a read-only prototípust. 5.1 A REST WCF projekt létrehozása Létrehoztam egy üres WCF
projektet, egy WebHttp Service-t. A projekt neve WebService, továbbá a beüzemelt alkalmazás példányok neve is ez lett a megfelelő tartományban. Az üres projektben a következő névtereket hoztam létre a készülő komponensek számára: • Collections – az alaprendszer komponenseinek gyűjtemény típusai kerültek ide. Azért volt szükségem új gyűjtemény típusokra, mert így egyszerűen lehetett konfigurálni a reprezentációt, továbbá üres gyűjteményt is vissza tudtam adni a kliensnek. • Composites – ide a kompozit erőforrások komponensei kerültek. • SystemComponents – a rendszer komponensek, melyek a szolgáltatás működéséért felelősek – tehát nem erőforrások – ide kerültek. A célom az volt, hogy az alaprendszert minél kisebb mértékben módosítsam. Ezért létesítettem ezek az új komponensek a kliens projektbe. A szolgáltatások a projekt gyökerébe lettek helyezve. 5.2 Az erőforrások meghatározása Az
erőforrások meghatározásánál nagy szerepet játszanak a kliensek, hiszen a szolgáltatásnak az ő igényükhöz kell igazodni. A kivitelezés ezen fázisában összeszedtem a rendszerem azon témaköreit, amelyeket közzé akartam tenni, illetve feltérképeztem, hogy milyen új komponensekre lesz még szükség. Megvizsgáltam, hogy a meglévő 33 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben komponenseken milyen módosításokat kell végeznem, továbbá milyen speciális lekérdezéseket kell megírnom. Olyan témaköröket, amelyekre feltételezhetően nincs szükségük a klienseknek, nem dolgoztam fel a munkám során. Készültek új komponensek is, mint speciális gyűjtemény típusok és kompozit-ok. Ebben a fázisban már gondolkodni kezdtem a reprezentáción és az erőforrások összekapcsolásán is. A web-szolgáltatásban szereplő erőforrások javarészt adatbázis rekordjai, de vannak bináris és dinamikusan, algoritmus
útján létrehozott erőforrások is. 5.21 Az érintett témakörök A következő témakörök lettek közzétéve, mint erőforrások: • Termékek (Product) • Gyártók (Manufacturer) • Termék státuszok (ProductState) • Termékfajták (ProductType) • Terméktémakörök (ProductSubject) • Termékképek (ProductImage) • Mértékegységek (Measure) A fenti témakörök adatbázis rekordjai és ilyen erőforrásokat képzelhetünk el, például: • A 10851-es azonosítóval rendelkező termék • A „ARG556” cikkszámú termékek listája • A gyártók listája • A termékfajták és azok terméktémakörei • A 10851-es azonosítójú termék nyilvános termékképei A fenti erőforrások segítségével a kliensek egy egyszerű termékkeresőt tudnak létrehozni. De volt példa arra is, hogy jelentés generáló alkalmazás (report application, mint az SQL Server Reporting Services) által készített kimutatáson, vagy nyomtatott
katalógusban lettek felhasználva. Mivel a fenti témakörök egyenként és gyűjteményként is elérhetők (például termék és termékek), ezért a rendszerbeli komponensekhez a WebService projekt Collections névterébe gyűjtemény típusokat hoztam létre. 34 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben [CollectionDataContract(Namespace = "")] public class Manufacturers : List<Manufacturer> { . public Manufacturers(IEnumerable<Manufacturer> m) : base(m ?? new Manufacturer[] { }) { } } 11. ábra A CollectionsManufacturers gyűjtemény A Collections.Manufacturers (11 ábra) zárt SystemCollectionsGenericsList<T> típus A paraméteres konstruktorban a konstruktorláncolásnál látható egy kis trükk, ahol null-os konstrukciós gyűjteményt egy üres tömbbel helyettesítek. Ez azt a célt szolgálja, hogy egy üres Manufacturers gyűjtemény jöjjön létre akkor is, ha az üzleti logikai rétegben a lekérdezés nem ad
vissza eredményt. Így az erőforrás egy üres Manufacturers objektum lesz és egyetlen sor kóddal el lehet intézni a gyártók lekérdezését (12. ábra), ezáltal karbantarthatóbb és olvashatóbb kódot kapunk. new Manufacturers(Products.ManufacturerGetManufacturers<ProductsManufacturer>()); 12. ábra A CollectionsManufacturers használata Továbbá vannak olyan erőforrások, melyek a kliensek igényei révén születtek meg: 1. Termékek cikkszámai, karakterlánc prefix alapján Ezt az erőforrást termékkereső weboldalon, cikkszám kereső mező automatikus kiegészítéséhez használják – autocomplete. Ezek a terméktörzsből lesznek kinyerve A CollectionsItemNumbers egy zárt, System.String alapú gyűjtemény, ahol deklaratívan, explicit definiáltam a gyűjtemény elemeinek nevét a kimenetben, mint ItemNumber. A Data Contract sorosítás esetén a gyűjtemény típus neve lesz a gyökér elem. [CollectionDataContract(Namespace = "",
ItemName="ItemNumber")] public class ItemNumbers : List<string> { . } 13. ábra A CollectionsItemNumbers gyűjtemény 2. Egy termék vonalkódja Az alaprendszer több vonalkódot is el tud tárolni egy termékhez, és azt több formátumban (EAN-8, EAN-13 stb.), több verzióban Ez az erőforrás dinamikusan legenerált bináris kép, amit például így is meg lehet nevezni: „a 10851-es ID-jű termék alapértelmezett vonalkódja, 300 pixel széles és 100 pixel 35 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben magas képként, .jpg formátumban” Ez az erőforrás nyomtatott termékkatalógusokban lett alkalmazva. A termékek többi vonalkódja, vagy épp egy termék összes (nem alapértelmezett) vonalkódja erőforrásként nem elérhető, hiszen sok hasznuk nincs. Egy katalógusban a termék akkori alapértelmezett vonalkódját kell megjeleníteni. 5.22 Kompozitok Egy termékkereső weblap elkészítéséhez a fenti erőforrások
ugyan elegendő támogatást adnak, de teljesítmény szempontból nem megfelelőek. Mivel ha egy termékkereső készül, és lekérdezzük * mennyiségű termék adatait, akkor alkalommal kellene lekérdezni azok termékképeit és keresztreferencia cikkszámait (14. ábra) 14. ábra Termékadatok lekérdezése kompozit erőforrás nélkül A Composites.DetailedProductInformation kompozit és annak gyűjtemény típusa (15 ábra) egy erőforrásban állítja rendelkezésre a termékadatokat, a termékképeket és a keresztreferencia cikkszámokat. 36 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 15. ábra A részletes termékinformáció kompozit Ez a konstrukció javít a rendszer átviteli sebességén, továbbá a kliensek is könnyebben fel tudják dolgozni azt. 16. ábra A kompozit erőforrás lekérdezése hatékonyabb Kompozitokat objektum-orientált környezetben nem csak csomagoló komponensekkel (wrapper) hanem örökléssel is elő lehet
állítani. Jelen esetben lehetőségem nyílt volna a Products.Product komponensből örökölni és a leszármazottat kibővíteni Átvitt értelemben maga a Products.Product is kompozit, erre a gyorsítótárazásról szóló fejezetben visszatérek 37 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5.23 A közös interfész A korábban ismertetett, erőforrásként felhasznált komponenseket ezt követően egy újabb, saját interfésszel láttam el. Az interfész neve CommonTypesWebResourceIWebResource és a CommonTypes szerelvényben helyeztem el (17. ábra) 17. ábra A webes erőforrásnak szánt komponensek interfésze Minden szóban forgó komponens közvetlenül implementálja ezt az interfészt. Legfontosabb tulajdonsága a bool IsPartial, ami ha igaz értékű, akkor az objektum csak részlegesen lett inicializálva az adatbázisból, tehát ha PUT-tal elmentik a részleges objektumot, akkor adatveszetést is előidézhetnek7. Az egységes
REST interfész implementálásakor és az erőforrás összekapcsoláskor visszatérek erre a típusra. 5.3 Az URI-k megtervezése Miután meghatároztam az erőforrásokat, az azonosításuk már nagyon könnyű volt. Figyelembe vettem a témakörök elnevezéseit és azonosítóit, melyek legtöbb esetben egész számok. Csak olyan erőforrás azonosítókat hoztam létre, melyek hasznosak lehetnek a kliensek számára. A tervezéskor erőforrások azonosítóit kellett leírnom táblázatosan, például: 1. a 10851-es termék 2. a „ARG556” cikkszám töredékkel hasonuló cikkszámú termékek 3. a „ARG556” cikkszám töredékkel hasonuló cikkszámú és a 10-es gyártótól származó termékek 4. az első tíz felújított termék 7 Az IsPartial nem azt a feladatot tölti be, mint a 206 („Partial Content”) HTTP státusz kóddal jelzett válasz. Az a feltételes GET műveleteknél használatos, melyet a dolgozatom nem érint. 38 Pótári Tamás RESTful
web-szolgáltatások készítése WCF-ben Majd miután új témakör esetén is az erőforrásokon dolgoztam, újra visszatekintettem a korábbi erőforrásokra, így ezek lehetséges és hasznos kombinációjából újabb erőforrások születtek: 5. a 15-ös termékkép 6. a 10851-es termék képei A fenti erőforrásokból a következő lehetséges URI-k születtek (számozás szerint): 1. /WebService/Product/10851 2. /WebService/Product?itemNumber= ARG556&forceItemNumberEquality=false 3. /WebService/Product?itemNumber=ARG556&forceItemNumberEquality=false &manufacturerID=10 4. /WebService/Product?productStateID=2&rowCount=10 5. /WebService/ProductImage/15 6. /WebService/Product/10851/ProductImages vagy /WebService /ProductImage?productID=10851 A lehetséges URI-ket homogénekké alakítottam, majd URI mintákat (uri template) hoztam létre belőlük. 5.31 Az URI template-ek megtervezése A lehetséges URI-ket útszegmensekre bontottam, és ahol értelme volt, a
szegmenseket paraméterekké alakítottam. Konstans (literál) szegmensek lettek a témakör nevek és maga az alkalmazás neve is. A többi paraméterként kezelt szegmensből literál, query string vagy útszegmens változó lett (18. ábra) 39 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 18. ábra Az URI template fa 5.32 Az URI template-ek implementálása Az alkalmazásban ezt követően hoztam létre a szolgáltatások törzseit. Minden témakörnek lett saját szolgáltatás komponense azaz ServiceContract-ja. Ekkor még csak readonly szolgáltatáson implementáltam a template-eket System.ServiceModelWebWebGetAttribute-okkal (19 ábra) A ProductBaseService osztály, melyből minden szolgáltatásom öröklődik, saját készítésű és apróbb szolgáltatásokat nyújt. A Global.asax-ban van megadva az útválasztás, ami a kérés URI-jéből határozza meg, hogy melyik szolgáltatást kell meghívni. Ez egy jól ismert és bevált megoldás
az ASPNET-ben, mely segítségével barátságos URI-ket lehet létrehozni URI újraírás helyett. Ahány szolgáltatás elérhető, minimum annyi Routing szükséges [32]. A Routing-ok ezekre a ServiceContract-okra irányítják a kéréseket és a témakörök elnevezéseire épülnek. 40 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class ProductService : ProductBaseService { [WebGet(UriTemplate = "/{id}")] public Products.Product GetProduct(string id) { . } [WebGet(UriTemplate = "?itemNumber={itemNumber}&forceItemNumberEquality={forceItemNumberEquality}& productTypeID={productTypeID}&productSubjectID={productSubjectID}&productStateID={productStateID}&manu
facturerID={manufacturerID}&name={name}&rowCount={rowCount}&pageNumber={pageNumber}&relationIDs={relat ionIDs}&reverseLookup={reverseLookup}")] public Collections.Products GetProducts(string itemNumber, bool forceItemNumberEquality, int productTypeID, int productSubjectID, int productStateID, int manufacturerID, string name, int rowCount, int pageNumber, string relationIDs, bool reverseLookup) { . } [WebGet(UriTemplate = "/{id}/ProductImages")] public ProductImages GetProductImages(string id) { . } . } 19. ábra A Product erőforrás read-only szolgáltatása 5.4 Az egységes HTTP interfész implementálása Az alaprendszer szolgáltatásainak köszönhetően nagyon kevés munkába került az egységes interfész implementálása. A korábbi lépésemben már létrehoztam a read-only funkcióit a szolgáltatásoknak, ezt követően a többi, módosító műveletet is implementáltam. Sorba rendeztem az összes erőforrás URI-t – pontosabban URI
template-eket – egy táblázatba, majd meghatároztam, hogy mely URI-ket milyen műveletekkel szándékozom ellátni. Erőforrás URI /Product POST PUT GET * /Product/{id} * * /Product/{id}/ProductImages * /Product/?itemNumber={itemNumber}. * /Manufacturer * /Manufacturer/{id} /ProductType /ProductType/{id} /ProductType/JustActives DELETE * * * * * * * * * * * 41 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben /ProductType?productSubjectID={pID} /ProductState * * /ProductState/{id} * * /ProductState/JustActives /ProductSubject * * * * /ProductSubject/{id} * * * /ProductSubject/{id}/ProductTypes * /ProductSubject/JustActives * /ProductImage/{id} * /ProductImage?productID={productID} * /Measure * /Measure/{id} * /DetailedProductInformation?itemNumber={itemNumber}. * /ItemNumber/{prefix} /ProductBarcode/Product/{productID}.{format}?width={w}&hei ght={h} * * * 20. ábra Az URI template-ek és azok műveletei
Mint ahogy a 20. ábra is mutatja, a legtöbb erőforrás csak olvasható A lekérdezés alapú erőforrásokon nincs értelme módosítást használni. Illetve a rendszer működéséhez szükséges komponensek módosítását sem engedélyezem. Ilyen a mértékegység (Measure), melyet több más témakör is használ, olyanok is, melyek nincsenek erőforrásként közzétéve. Ezen témakörök módosítása befolyásolhatja az alaprendszer viselkedését. A POST és PUT műveletek visszaadják az újonnan létrehozott erőforrásokat. Így elérhetővé válnak az új, rendszer által generált azonosítók is. A bool ISavingServicesIsNew tulajdonsággal üzentem az üzleti logikai rétegnek, hogy objektumlétrehozás vagy módosítás történjen. Ugyan a PUT és a DELETE műveletek idempotensek, mégis 404-es üzenetet kell majd visszaküldeniük, ha az érintett erőforrás nem létezik. 42 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben [ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class ManufacturerService : ProductBaseService { [Description("List of manufacturers.")] [WebGet(UriTemplate="")] public Manufacturers GetManufacturers() { return new Manufacturers(Products.ManufacturerGetManufacturers<ProductsManufacturer>()); } [Description("A manufacturer.")] [WebGet(UriTemplate = "/{id}")] public Products.Manufacturer GetManufacturer(string id) { return new Products.Manufacturer(intParse(id)); } [Description("Creates a new manufacturer.")] [WebInvoke(Method="POST", UriTemplate="")] public Products.Manufacturer CreateManufacturer(ProductsManufacturer m) { m.IsNew = true; m.Save(); return m; } [Description("Updates a manufacturer.")] [WebInvoke(Method = "PUT", UriTemplate = "/{id}")] public
Products.Manufacturer UpdateManufacturer(string id, ProductsManufacturer m) { m.IsNew = false; m.ID = intParse(id); m.Save(); return m; } [Description("Removes a manufacturer.")] [WebInvoke(Method = "DELETE", UriTemplate = "/{id}")] public void DeleteManufacturer(string id) { (new Manufacturer(int.Parse(id)))Delete(); } } 21. ábra A gyártó erőforrás szolgáltatása 5.5 A reprezentáció megtervezése A kivitelezés korábbi fejezeteiben nem tértem ki a reprezentációra. Legfeljebb az URIknél jöhetett volna szóba, ha az URI-ben kapta volna meg a szolgáltatás a kívánt formátumot (.json, xml stb) De a Data Contract sorosítónak hála, eddig csak komponensekkel kellett „legóznom”. A reprezentáció tervezésekor határoztam meg, hogy a komponensek mely tulajdonságai kerüljenek sorosításra és melyek ne. Továbbá milyen részletességgel legyenek feltöltve az 43 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben
objektumok, és hogyan jelezzem a klienseknek, hogy egy objektum 100%-ban az adatbázisbeli állapotával került-e le hozzájuk vagy csak részlegesen. 5.51 A Data Contract dekoráció és részleges objektumok A reprezentációt következetesen terveztem meg. A Data Contract-nak köszönhetően az erőforrásokat homogén reprezentációval láttam el. Így ha egy termék objektummal találkozik a kliens oldali fejlesztő egy erőforrásban – például a jövőben egy árajánlat erőforrásban –, akkor az pontosan ugyanolyan szerkezetű lesz, mint maga a termék erőforrás, legfeljebb kevesebb adat lesz abban. Az erőforrások tartalma a korábban említett bool CommonTypes.WebResourceIWebResourceIsPartial tulajdonságtól függ Ez a megoldás az átvitt adat mennyiségének csökkentése érdekéből született meg. Ha ez a tulajdonság igaz, akkor az erőforrás minden tulajdonsága belekerül ugyan a kimenetbe – xml esetén xml elemekként – de nem
mindegyiküknek lesz értéke. Továbbá, ha egy nagyobb objektum gráfot, például egy termék erőforrást kérdezünk le, akkor annak gyermekobjektumai garantáltan részlegesek lesznek. <ProductState xmlns:i="http://www.w3org/2001 /XMLSchema-instance"> <Description>Original products</Description> <ID>2</ID> <IsActive>true</IsActive> <IsPartial>false</IsPartial> <Links/> <Name>Original</Name> </ProductState> <Product xmlns:i="http://www.w3org/2001/XML Schema-instance"> . <IsPartial>false</IsPartial> <ItemNumber>0004230701</ItemNumber> <ProductState i:type="ProductState"> <Description/> <ID>2</ID> <IsActive>true</IsActive> <IsPartial>true</IsPartial> <Links/> <Name>Original</Name> </ProductState> . </Product> 22. ábra A termék státusz objektum teljes és részleges
feltöltéssel A 22. ábra két XML reprezentációját jeleníti meg a ProductsProductState típusú objektumnak. A bal oldali esetben az IsPartial tulajdonság hamis, és így a Description értéke ki lett olvasva az adatbázisból. Míg a jobb oldalon a Product erőforrásban lévő ProductState Description-jének értéke hiányzik, és az IsPartial igaz értékű. Ha a kliensnek mégis szüksége lenne a ProductState.Description-re, akkor a következő fejlesztési fázisban implementált linkekkel le tudja kérdezni az egész ProductState erőforrást, ami garantáltan 100%-ban inicializált. 44 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben A komponensek bool CommonTypes.ISavingServicesIsNew tulajdonságait rendre kihagytam az erőforrásokból a System.RuntimeSerializationIgnoreDataMemberAttribute-tal Csak tulajdonságok lettek DataMemberAttribute-tal ellátva. 5.52 A kívánt reprezentáció megadása Mint ahogy a WCF fejezetben is szó volt róla,
WebHttp Service-ek nagyon ügyesen fülelik a kívánt reprezentációt. Alapesetben a Content-Type header-ben várja a szolgáltatás a kívánt formátumot, ami lehet application/xml (ez az alapértelmezett) vagy application/json. A dinamikusan generált termék vonalkód esetén az URI-ben kell megadni a kívánt formátumot. Az Accept-Language header-ben várja a szolgáltatás a kívánt nyelvi kultúrát, ami rendszerparaméter és alapesetben en-GB. Mivel az ASPNET kompatibilitás engedélyezve van a szolgáltatásban, ezért a System.WebHttpContext komponens segítségével egyszerűen ki tudom nyerni a komplexebb, priorizált kultúra paramétereket is: „en-GB,en;q=0.8,enus;q=05” Thread.CurrentThreadCurrentCulture = ThreadCurrentThreadCurrentUICulture = new System.GlobalizationCultureInfo(HttpContextCurrentRequestUserLanguages[0]); 23. ábra A nyelvi kultúra beállítása a megadott nyelvi paraméterrel A kívánt pénznemet egyedi Accept-Currency header-rel
állítható be. A rendszerparaméter alapértéke EUR. <Product xmlns:i="http://www.w3org/2001/XMLS chema-instance"> <ID>10851</ID> . <ListPrice xmlns:a="urn:Prices-Schema"> <a:Currency>EUR</a:Currency> <a:IsDefaultCurrency>true</a:IsDefaultCur rency> <a:Value>159.5000</a:Value> </ListPrice> . </Product> <Product xmlns:i="http://www.w3org/2001/XMLS chema-instance"> <ID>10851</ID> . <ListPrice xmlns:a="urn:Prices-Schema"> <a:Currency>HUF</a:Currency> <a:IsDefaultCurrency>false</a:IsDefaultCu rrency> <a:Value>47487.9350</a:Value> </ListPrice> . </Product> 24. ábra Egy termék EUR és HUF listaára 45 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5.53 Nagyobb gyűjtemény jellegű erőforrások lapozhatósága Munkáim során több alkalommal is találkoztam lapozással,
amikor is egy nagyobb terjedelmű entitásgyűjteményt oldalanként lehet lekérdezni. Sajnos sok REST (de kevésbé RESTful) szolgáltatás esetén találkoztam kényelmetlen, butácska megoldásokkal. Bizonyos esetben csak azt lehetett megadni az URI-ben, hogy hány rekord – például termék objektum – szerepeljen az erőforrásban. Ennek az a hátránya, hogy ha a keresés eredménye 500 rekord, akkor mind az ötszázat le kell kérdeznem – például rowCount=500 – és a kliens oldalon kellett imitálnom a lapozást. Egy másik rendkívül problémás megoldás az általam „nagyítós lapozásnak” elnevezett módszer, melyet egy világhírű ügyfélrelációs rendszer API-ja alkalmazott. Ilyenkor egy képzeletbeli, konstans d „átmérőjű” nagyítóval lehet végigpásztázni a lekérdezés rekordjait. Csupán a nagyító és az első rekord közötti távolságot lehetett megadni az URI-ben, mint offset – nullától indexelve (25. ábra) Például ha az offset
=1 akkor a 1-25 rekordokat olvashattuk ki. 25. ábra A nagyítós lapozás rugalmatlan és veszélyes A gond akkor következett be, amikor az API fejlesztői utólag megváltoztatták a d értékét 25-ről 50-re a háttérben úgy, hogy a klienseket nem értesítették. Így a kliensoldali 25 rekordonkénti lapozás implementációk – ahol az offset léptéke 25 szorzatai voltak – redundáns módon nyerték ki az adatokat, továbbá lemaradtak erről az optimalizálási lépésről, aminek valószínűleg hasznát látták volna. Tehát ez a lapozás nemhogy rugalmatlan, de még veszélyes is. A web-szolgáltatásomban következő URI-jű erőforrások lapozhatók: • /DetailedProductInformation?itemNumber={itemNumber}. • /Product?itemNumber={itemNumber}. 46 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Az én megoldásom sztenderd és rugalmas. Meg lehet adni az URI-ben egy oldal rekordjainak számát (rowCount), és a kívánt oldal sorszámát
(pageNumber, nullától indexelt). Például /Product?itemNumber=11221&rowCount=25&pageNumber=1, ami a 11221 cikkszám-töredékű termékek listáját 25 elemű oldalakon, a második oldalt jelenti. RESTful szolgáltatásokban gyakran meg lehet találni az erőforrásban az összes rekord számát, és a linkeket a következő vagy előző oldalakhoz is. Sajnos a Data Contract sorosítás miatt nem tudtam ezeket az extrákat elhelyezni a kimenetben [33]. 5.6 Összekapcsolhatóság A WCF WebHttp szolgáltatások esetén nincs sztenderd megoldás az erőforrás összekapcsolások implementálására. A Data Contract sorosítás miatt a fejlesztők a komponensekben helyezik el a link-eket, mint tulajdonságok. A készülő web-szolgáltatás is így csinálja (26. ábra) 26. ábra Az IWebResource és link-elés Az erőforrásoknak nem csak egy link-jük lehet, hanem akár több is, erről az általam tervezett IWebResource interfészben lévő Links gyűjtemény típusú
tulajdonság gondoskodik. Elemei CommonTypes.WebResourceLink8 példányok 8 A továbbiakban csak Link-ként nevezem meg a típust és példányát. 47 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5.61 A Link-ek inicializálása A probléma a legtöbb fejlesztő számára a link-ek inicializálása, főleg minimum kétrétegű architektúránál. A készülő szolgáltatásomban is az volt a cél, hogy elkülönítsem az üzleti logikai réteget és a web-szolgáltatás rétegét. Azaz, hogy az üzleti logikai réteg „minél kevesebbet tudjon” a külvilágról, a kliens kódról. Tehát nem lett volna ügyes dolog, ha az URI-ket beégettem volna a business rétegbe, mert így a kötetlen elhelyezkedésnek mondtam volna ellent. Csak a legfelső réteg – jelen esetben a web-szolgáltatás – ismeri az erőforrások URI template-jeit. Továbbá a business rétegből érkeznek az erőforrások azonosítói, melyek szükségesek, hogy az URI
template-ekből használható URI-k készüljenek. Tehát elég nehéz elkerülni azt, hogy az üzleti logikai réteg ne szerezzen tudomást a sitemap-ról. Az egyik megoldás a linkek inicializálására, ha azt a klienskódban végezzük el az objektumok bejárásával. Így a business rétegnek fogalma se lesz a Link-ek inicializálásának módjáról, ellenben az eljárás lassabb lehet az objektum gráfok bejárása miatt, illetve a klienskód karbantartása is nehézkesebbé válik (27. ábra) 27. ábra Nem elegáns megoldás a Link-ek utólagos inicializálása A web-szolgáltatásban provider-modellt alkalmazok, és az erőforrásként felhasznált komponensek belülről hívják a provider-t. A provider a CommonTypes.WebResourceLinkProvider absztrakt osztály (28 ábra), melynek statikus Current tulajdonságának fogja értékül adni a klienskód a LinkProvider implementációt az alkalmazás indulásakor. Majd a komponensek Links CreateLinks(object caller)
példányszintű metódust hívják a saját Link-jeik legenerálásához. 48 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 28. ábra A LinkProvider, amit az üzleti logikai réteg használ Az IWebResource.Links tulajdonságot a komponensek azonosítóiban állítom be, már ha van megadva LinkProvider (29. ábra) Ezáltal az adatbázisból történő kiolvasáskor is azonnal elkészülnek a Link-ek. namespace Products { /// <summary> /// A gyártó komponens. /// </summary> [Serializable] [DataContract(Namespace="")] [KnownType(typeof(Manufacturer))] public class Manufacturer : IManufacturer, IWebResource { . [DataMember] public int ID { get { return ID; } set { checkID(value); ID = value; if (LinkProvider.Current != null) Links = LinkProvider.CurrentCreateLinks(this); } } . } } 29. ábra A Link-ek beállítása a ProductsManufacturerID set-ben A web-szolgáltatás LinkProvider-e a Global.asax-ban lesz beállítva a
CommonTypes.WebResourceLinkProviderCurrent-re A LinkProvider implementáció neve a web-szolgáltatás projektben: SystemComponents.WebServiceLinkProvider A provider a hívó objektum típusát vizsgálja és az annak megfelelő Link-eket adja értékül a Links tulajdonságához (30. ábra) 49 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben public class WebServiceLinkProvider : LinkProvider { public override Links CreateLinks(object caller) { string host = OperationContext.CurrentHostBaseAddresses[0]AbsoluteUriReplace( OperationContext.CurrentHostBaseAddresses[0]AbsolutePath, ""); string appName = ConfigurationManager.AppSettings["ApplicationName"]; if (!string.IsNullOrEmpty(appName)) host = host + "/" + appName; Links l = new Links(); if (caller is Products.Product) { l.Add(new Link { Relation = "self", Uri = string.Format("{0}/Product/{1}", host, ((Products.Product)caller)ID) }); } else if (caller is
Products.Manufacturer) { l.Add(new Link { Relation = "self", Uri = string.Format("{0}/Manufacturer/{1}", host, ((Products.Manufacturer)caller)ID) }); } . 30. ábra A WebServiceLinkProvider 5.62 Link relációk A Link-ek relációkkal rendelkeznek, és ez rendkívül fontos egy RESTful szolgáltatás esetén. A Link tartalma hasonló az Atom link-hez [34] A reláció azt határozza meg, hogy • milyen erőforrásra hivatkozik az URI • mi a jelentősége a linknek • milyen jellegű műveletet tud elvégezni a kliens az URI erőforrásán • melyek a támogatott reprezentáció formátumai az erőforráshoz intézett kéréseknek és válaszoknak A legtöbb erőforrás csak egy „self” Link-kel rendelkezik, ami önmagára mutat. Ez rendkívül hasznos, ha termékek gyűjteményének erőforrásában az egyik termékről több információt akarunk kinyerni. A „self” Link segítségével teljesen feltöltött, IsPartial=false erőforráshoz
jutunk. Viszont vannak erőforrások, mint a ProductsProductImage, ahol három Link is található (31. ábra): • self – a szóban forgó ProductImage erőforrásra mutat • image – a bináris termékképre hivatkozik • thumbnail – a termékkép kicsinyített változatára hivatkozik (bináris) 50 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben <ProductImage> . <FileName>0000010504.jpg</FileName> <ID>10504</ID> <IsPartial>true</IsPartial> <IsPrinterFriendly>false</IsPrinterFriendly> <Links> <Link> <Relation>self</Relation> <Uri>http://localhost/WebService/ProductImage/10504</Uri> </Link> <Link> <Relation>image</Relation> <Uri>http://localhost/Ps/0000010504.jpg</Uri> </Link> <Link> <Relation>thumbnail</Relation> <Uri>http://localhost/Ps/0000010504 tn.jpg</Uri> </Link> </Links>
<Product i:nil="true"/> <ThumbnailFileName>0000010504 tn.jpg</ThumbnailFileName> </ProductImage> 31. ábra Egy ProductImage erőforrás és a linkjei Karbantarthatóság és skálázhatóság szempontjából érdekesség, hogy a bináris termékképek egy virtuális mappából érhetők el és a web-szolgáltatástól függetlenül működnek. Egy RESTful szolgáltatásnál természetes, hogy a szolgáltatás erőforrásain túl, máshonnan származó erőforrásokat is linkekkel teszünk közzé. És ha a jövőben egy újabb független erőforrást akarok „belinkelni” a ProductImage-be, akkor csak a LinkProvider-ben kell ezt beregisztrálnom. 5.7 Hitelesítés A web-alkalmazásom API kulcsos hitelesítést használ és minden request-ben meg kell adni a kulcsot, ami az egyszerűség kedvéért egy GUID. Az Authorization fejléc például így nézhet ki: Authorization: APIKey 25c45ece-f314-4194-b986-57564ffbdd6k. Az erőforrások /help
erőforrásait Authorization fejléc nélkül is lehet látogatni, azaz nem igényelnek hitelesítést. 5.71 Az API kulcsos hitelesítés implementálása Az alaprendszerben vannak regisztrálva az API kulcsok. Ott lehet őket legenerálni – esetleg újragenerálni – és biztonsági szerepkörökkel ellátni. Továbbá egy API kulcs 51 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben szabályozva van, hogy milyen termékeket lehet lekérdezni vele. Ez a beállítás is bármikor módosítható az alaprendszer web-es felületén. Csekély fejlesztési munkát rótt a vállamra az API kulcsok kezelése az alaprendszerben. A web-szolgáltatásban ServiceAuthorizationManager standard leszármazottat módon System.ServiceModel készítettem, melynek bool CheckAccessCore(OperationContext operationContext) felülírt metódusában végeztem el az API kulcs kiolvasást, hitelesítést és a biztonsági szerepkörök beállítását a megadott ügyfélre
[35] [36]. Az implementáció a SystemComponentsAPIKeyServiceAuthorizationManager Ezt úgy helyeztem el a konfigurációban, mely minden service-re érvényes legyen. <system.serviceModel> <behaviors> <serviceBehaviors> <behavior> <serviceAuthorization serviceAuthorizationManagerType="SystemComponents. APIKeyServiceAuthorizationManager, WebService" principalPermissionMode="None"/> 32. ábra Az API kulcsos hitelesítés beállítása a konfigurációban 5.8 Állapotkódok A WCF WebHttp szolgáltatások 200-as HTTP kóddal válaszolnak, ha sikeresen volt a kérelem. Sajnos új entitás létrehozásakor is ezt kapjuk 201 (Created) helyett Legtöbb hiba esetén 400-at kapunk. Sajnos visszaállítási (deserialization) hibák esetén is ugyanezt– például PUT vagy POST műveletnél – és hibás vagy hiányzó content-type-ok esetén is. 404 amikor a kérelem egyetlen URI template-nek sem felel meg, és 405-öt, ha olyan HTTP
műveletet akar végezni a kliens az erőforráson, ami nem támogatott [37]. Továbbá lehetőségünk van System.ServiceModelWebWebFaultException-t System.ServiceModelWebWebFaultException<T>-t dobni státuszkóddal Az utóbbihoz sorosítható objektumot is csatolhatunk, ami a response-ba belekerül. 5.81 Globális hibakezelés A web-szolgáltatásomban a fenti státuszkód kezelés nem elegendő. A keletkezett kivételeket a rendszeremnek adatbázisbeli hibanaplóba és email üzenetként az 52 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben adminisztrátorokhoz is el kell juttatnia. Illetve nem akartam minden service metódusba kivételkezelést használni. Ennek érdekében létrehoztam egy System.ServiceModelDispatcherIErrorHandler implementációt, mint SystemComponents.ErrorHandler Ez a komponens globálisan elkapja a web-szolgáltatásban keletkező kivételeket, így azokat meg tudja vizsgálni és fel tudja dolgozni. Illetve egyéni
hibakódokként tudom eljuttatni a kliensekhez, beszédes hibaüzenettel. Ezt a komponenst egy SystemServiceModelDescriptionIServiceBehavior implementációval helyeztem el a konfigurációban, minden szolgáltatás viselkedéseként. Különösen odafigyeltem az adatbázisból érkező hibaüzenetek elfedésére. Ugyanis egy rossz szándékú kliens hasznos információkra tehet szert. Ezért csak egy „An error occured” üzenet jelenik meg 400 kíséretében. Ha egy lekérdezés előreláthatóan rengeteg adattal térne vissza, vagy timeout keletkezik az SqlClient-ben, akkor 503-as hibakód a válasz. A CommonTypes.ObjectNotFoundException-re 404-gyel és lokalizált üzenettel válaszol az szolgáltatás. Az explicit dobott SystemServiceModelWebWebFaultException kivételeket változatlanul továbbítja a kezelő. 5.82 A 201 státusz kód A WCF POST művelet esetén nem állít elő 201-es kódot és Location fejlécet, ezt a programozónak kell elvégeznie – már ha RESTful
szolgáltatáson dolgozik. Ezt a LinkProvider-nek hála, nagyon könnyen el lehetett System.ServiceModelWebOutgoingWebResponseContext érni (33. ábra). rendelkezik A egy SetStatusAsCreated(Uri) metódussal, ami beállítja a Location fejlécet és a 201-es státuszkódot [38]. [Description("Creates a new manufacturer.")] [WebInvoke(Method = "POST", UriTemplate = "")] public Products.Manufacturer CreateManufacturer(ProductsManufacturer m) { m.IsNew = true; m.Save(); if (m.Links != null && mLinksCount > 0) { var uri=m.LinksFirstOrDefault(l => lRelation == "self"); if(uri!=null) WebOperationContext.CurrentOutgoingResponseSetStatusAsCreated(new Uri(uriUri)); } return m; } 33. ábra 201 státuszkód és Location header beállítása a self Link-kel 53 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5.9 Dokumentáció A szolgáltatások dokumentációit a WCF dinamikusan állítja elő. Ehhez csupán
a metódusokhoz System.ComponentModellDescriptionAttribute attribútumot kellett rendelni A dokumentáció angol nyelvű. A 34 ábra a Product/help erőforrást ábrázolja A Method oszlop valamely linkjére kattintva egy részletező oldal jelenik meg, amin XML és JSON minta adatok, továbbá XML sémák jelennek meg. 34. ábra A Product témakör dokumentációja Az automatikus help generáló némi kínos hiányossággal rendelkezik. Olyan objektumhoz, melyek System.FlagsAttribute attribútummal ellátott felsorolás típusra hivatkozik, a help generáló nem tud XML sémát és példakódot generálni. Ez objektum gráfra is igaz, ha annak bármely objektumára jellemző a fenti eset. Ilyenkor az enum típusú mezőt ki kell hagyni a sorosításból. Továbbá készült egy gyökér oldal a dokumentációhoz, ahol általános szabályokat, HTTP header-eket dokumentáltam. A Helpaspx dinamikusan jeleníti meg a támogatott nyelvi kultúrákat, pénznemeket, melyek az alaprendszer
komponenseiből érkeznek és adatkötöttek (35. ábra) 54 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 35. ábra A Helpaspx a dokumentáció főoldala 5.91 Help Link-ek az erőforrásokban A kliens programozóknak nagyon hasznos lehet, ha az erőforrásokban a Link-ek között megtalálják a szóban forgó erőforrásra mutató /help oldalt is. Továbbá a help link-reláció ismert a web-programozók körében [39]. Nem vett igénybe sok munkát a Link-ek elhelyezése a LinkProvider-ben (36. ábra) <Product> . <ID>10851</ID> <Links> <Link> <Relation>self</Relation> <Uri>http://localhost:8800/Product/10851</Uri> </Link> <Link> <Relation>help</Relation> <Uri>http://localhost:8800/Product/help</Uri> </Link> </Links> . </Product> 36. ábra A termék erőforrás /help Link-je 55 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben
5.10 Tesztelés Teszteléshez a Visual Studio 2010 Ultimate teszt megoldásait, azok között is a Web Performance Test-et alkalmaztam, ami tipikusan webes rendszerekhez lett készítve. Segítségével webes UI-kat és web-szolgáltatásokat lehet tesztelni, nem csak üzleti logikai, hanem teljesítmény szempontból is – ez utóbbiról a következő optimalizálási fejezetemben írok. Továbbá igénybe vettem a CodePlex által felügyelt Web and Load Test Plugins for Visual Studio Team Test 9 ingyenes Visual Studio kiegészítőt. 5.101 Egységtesztek A teszteléskor az volt a tervem, hogy minden erőforráson egységtesztben vizsgálom az egységes interfész működését. A vizsgált szolgáltatással először létrehoztam egy új erőforrást POST művelettel, majd PUT-tal módosítottam, és DELETE-tel kitöröltem azt. POST és PUT műveleteknél megvizsgáltam, hogy az erőforrás tulajdonságai a kívánt módon lettek-e beállítva. Továbbá minden módosító
műveletet követően GET-tel lekértem a szóban forgó erőforrást, és összevetettem a módosító művelet által visszaadott erőforrással, már ahol volt ilyen. Ha az erőforrások egyeztek – például ha a POST művelet által visszaadott XML adat azonos volt a rákövetkező GET művelet által kinyert XML-lel – akkor sikeres volt a teszt. Továbbá vizsgáltam a státuszkódokat és a biztonsági megszorításokat is. A teszt végeredményben feltakarított maga után, így nem maradt szemét az adatbázisban. Ebben a fejezetben a Manufacturer erőforráson mutatom be a tesztelési módszerem és annak eredményességét. A 37. ábra mutatja az általam a Visual Studio-ban készített WebTest-et A teszt elkészítése egyszerű volt. Az első teszt a POST művelet tesztje, ami a válasz erőforrásból kinyerte az új gyártó ID-jét, és környezeti paraméterként közzétette a többi teszt számára. Így a többi teszt a ResourceUri és az ID paraméter
kombinációjából kapott URI-t10. A létrehozásnál és a PUT műveletnél is a String Body-ban definiáltam az XML erőforrást, felparaméterezve a megfelelő element-jeit a környezeti paraméterekkel (NewManufacturerName). A POST műveletnél a XPathValidationRule a válaszban kapott gyártó nevet összevetette környezeti paraméterével. A következő GET művelet pedig 9 http://teamtestplugins.codeplexcom/ Dolgozatom írásakor az 12-es kiadás volt aktuális Ez a paraméter alapú kérelemkonstruálás rendkívül rugalmassá tette a tesztjeimet, mivel elég volt csak a ResourceUri paraméter értékét átírnom és már a végleges web-kiszolgálón is tesztelhettem az alkalmazásomat. 10 56 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben kinyerte az újonnan létrehozott erőforrást és az XPathValidationRule-ja XPATH kifejezéssel összehasonlította azt a korábbi POST eredményével, amit ott NewManufacturer környezeti paraméterbe
helyeztem. Ez a láncolás lett alkalmazva a PUT-ra és a DELETE-re is Mint látható az ábrán, az Authorization fejlécet is környezeti paraméterrel inicializáltam minden kérésnél. [40] 37. ábra A Manufacturer szolgáltatás WebTest-je 5.102 Teszteredmények Az első lefutás sikeres volt. Mint ahogy a 38 ábra is jelzi, az első POST művelet 201-es kóddal válaszolt és NewManufacturerName megegyezett a POST válaszában lévő Manufacturer/Name értékével. A válasz mérete a rákövetkező GET művelet eredményének méretével pontosan megegyezett és az XPathValidationRule itt is sikeres volt. A legutolsó GET 404-et adott, jelezvén – magyar nyelvű hibaüzenettel is –, hogy az ezt megelőző DELETE kitörölte a példa adatot. 57 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 38. ábra Sikeres teszt, törlés után a legutolsó GET már nem találta meg a gyártót A következő teszt a biztonsági megszorításokat vizsgálta (39.
ábra) A háttérben megvontam az objektumok törlését engedélyező biztonsági szerepkört az API kulcstól. Így a DELETE teszt megbukott. 39. ábra Biztonsági teszt: az API kulcs nem tudott objektumot törölni A tesztek java automatizálva lett, de bizonyos nagyméretű, komplex erőforrásoknál manuálisan vetettem össze a kapott és várt eredményeket. A tesztelést követően egy egyszerű ASP.NET-es termékkeresőt is készítettem, mellyel a szolgáltatás használhatóságát vizsgáltam A példaalkalmazást az I. sz Mellékletben szemléltetem A kivitelezés utolsó lépéseként meggyőződtem arról, hogy a web-szolgáltatásom hibátlanul működik, a tesztelés során fellépő apróbb hibákat pedig javítottam. 58 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 5.104 Egy kis technológiai affér Teszteléskor egy nagyon különleges és kellemetlen hibájára jöttem rá a JSON sorosítónak. Ha egy dátum mező SystemDateTimeMinValue
értékre lett inicializálva, akkor a sorosító nem tudja azt feldolgozni. Sajnos a WCF ilyenkor csak egy 504-es hibaüzenetet ír, ezért a Visual Studio-ban található Service Trace Viewer segédalkalmazással vizsgáltam meg az esetet. A segédalkalmazásból a következő hibaüzenetet olvastam ki: „SerializationException: DateTime values that are greater than DateTime.MaxValue or smaller than DateTime.MinValue when converted to UTC cannot be serialized to JSON” Az interneten több fórum bejegyzés is vitatta az esetet, javaslatukra némi kontármunkával áthidaltam a problémát. Az inicializálatlan dátum mezőket ilyen alapértékkel kellett ellátnom: System.DateTimeMinValueToUniversalTime() Remélhetőleg a jövőben ezt orvosolja a Microsoft. 59 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 6. Telepítés és optimalizálás Az alaprendszert több vállalkozás is használja, más-más üzletágban. Az alkalmazások példányainak
legtöbbje azonos módon lett telepítve. Ebben a fejezetben az egyik példány esettanulmányát szemléltetem a tulajdonos cég kilétét, és üzletágát elfedve. A cégről annyit célszerű tudni, hogy a dolgozatom írása időpontjában közel harmadik éve használják a rendszert és körülbelül százezres elemszámú terméktörzzsel rendelkeznek. 6.1 Igény a szolgáltatása Az egyik magyarországi kereskedelmi cégnél felmerült az igény, hogy a saját termékeik reklámozásához új weblap készüljön, amely az információs rendszerük – tehát az alaprendszer egy példányának – adatbázisából lesz adatokkal ellátva. Az általam implementált API-t egy PHP alapú web-alkalmazás használta fel szerveroldalon termékkereső és részletező web-lapok előállításához. 6.11 Kialakítás és kommunikáció A termékismertető oldal kezdetben hazai szerveren futott, míg az információs rendszer és az API az Egyesül Államokban üzemeltetett
szervereken. A 40 ábra ábrázolja a szerverek és az alkalmazások elhelyezkedését, továbbá egy kérelem útvonalát 1-9 lépésekkel 11. Ez egy klasszikus megoldás a web-en, így rugalmas volt az összekapcsolás. Annyi kritérium volt csupán, hogy a termékismertető alkalmazás nem juttathatta el az API kulcsot a kliensoldalra, így annak léte csak a kliens szerveren volt ismert. Így mondhatni, hogy a végfelhasználók nem szereztek tudomást arról, hogy a háttérben mi zajlik. Szintén érdekesség, hogy a kliens web-alkalmazás több API kulcsot is használt a kérelmeihez. Ez azért volt hasznos, mert ugyanabból az adatbázisból az ismertetett terméknek, más gyártóktól származó helyettesítő termékeit is meg kellett tudni jeleníteni. Így a helyettesítő termékek keresésekor olyan API kulcsot alkalmaztak, mely úgy volt 11 A dolgozatom készítése alatt a kliens web-alkalmazás fejlesztés alatt állt. Így nem voltak pontos mérési eredményeim a
rendszerek átviteli teljesítményére vonatkozólag. Továbbá a kliens-alkalmazást külső beszállító készítette, melynek implementációja így fekete-doboz volt számomra. Az ábrázolt működésről a külső fejlesztőkkel folytatott megbeszélések útján szereztem információt. 60 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben konfigurálva, hogy csak a helyettesítő termékeket adja vissza. Az API kulcsok ezen beállítását pedig az alaprendszerben bármikor megváltoztathatták az adminisztrátorok. 40. ábra A kialakítás és egy kérelem útvonala 6.2 Gyorsítótárazás A REST szolgáltatások – és persze a web – legnagyobb erénye a cache-elés. Ez ma már egy jól ismert fogalom. Habár WCF alapú a megoldásom, mégis nagyon szoros a kapcsolata az ASP.NET-tel Ebben a korábban említett ASPNET kompatibilitás mód engedélyezése játszott közre. Így nem csak a routing hanem a professzionális ASPNET cache-elés is
elérhető a WCF szolgáltatásaim számára [41]. A tárazás nagyon szubjektív dolog. Gyakran változnak a beállítások a rendszer élettartama során. Ezért a következő fejezetekben a kiinduló beállításokat mutatom be 61 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 6.21 A átmenetileg tárolható erőforrások meghatározása A feladat során felsorakoztattam a szolgáltatásban jelenlévő erőforrásokat hasonlóan, mint az egységes interfész implementációjánál. És megvizsgáltam, hogy az egyes erőforrásoknak milyen frissesség élettartamot (freshness lifetime) célszerű adni. A tervem az volt, hogy megfigyelem, az adott cégnél egyes entitások milyen gyakorisággal változtak. A megfigyeléshez az SQL Server Profiler segédalkalmazást használtam. Az erőforrásokat terveim szerint osztályokba soroltam a frissességük szerint. Ezalatt a következőket vettem figyelembe: • a lekérdezés költségét – amely bizonyos
erőforrásoknál rendkívül nagy • az erőforrás méretét – mivel a szerver gyorsítótára is korlátozott méretű • az erőforrások változásának gyakoriságát • a gyorsítótárazás hasznosságát – tehát az erőforrás népszerűségét figyelembe véve továbbá azt is, hogy az erőforrás az API-n keresztül módosítható-e, ugyanis így adatvesztést is előidézhetnek a kliensek Az osztályok a következők lettek: • Pár órás (FewHours) • Egy napos (OneDay) • Egy hetes (OneWeek) • Cikkszámok (ItemNumber) Végeredményben a gyűjtemény jellegű erőforrások és a nagy számítás igényű erőforrások lettek optimalizálva. A kompozit erőforrások élettartamát a bennük található legkisebb élettartammal rendelkező részegység élettartama adta. A részletes termékinformáció erőforrásoknak maximum akkora élettartamuk lehet, mint a bennük lévő termék erőforrások élettartama, ami jelen esetben pár órás.
6.22 A cache-elés beállítása Az IIS 7.5 alkalmazás kiszolgálóban két féle cache-elési megoldás támogatott: a rendszermag módú (kernel-mode) és a felhasználói módú (user-mode). Az előbbi mód nem sok szabad kezet ad az adminisztrátoroknak, mivel ugyanazt az eltárolt választ küldi minden kliensnek, függetlenül a kérések közötti eltérésektől. A user-mode ügyesebb, mivel meg lehet 62 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben adni, hogy milyen verziókat tároljon el a kérelem paraméterei és fejlécei alapján. Viszont csak erőforrások fájlnév-kitejesítéseihez lehet ezt kötni. Az IIS szolgáltatásain túl, az ASP.NET alkalmazás konfigurációjában még pontosabban lehet definiálni az erőforrások cache-elését és osztályozást is végezhetünk. Első lépésben a cache osztályokat cache profile-okként beállítottam a konfigurációban (41. ábra) A location attribútummal határoztam meg, hogy a szerveren
és a kliensoldalon is gyorsítótárazva legyenek az erőforrások. <system.web> <caching> <outputCacheSettings> <outputCacheProfiles> <add enabled="true" name="OneWeek" duration="604800" location="ServerAndClient" varyByParam="*" varyByHeader="Accept-Language;Content-Type;Accept-Currency;Authorization" /> . 41. ábra Egy hetes élettartamot biztosító cache profil a konfigurációban A varyByParam attribútumot úgy állítottam be, hogy ne vegye figyelembe a query stringet, viszont a varyByHeader révén tegyen különbséget a reprezentáció formátuma és a hitelesítés módja között is. Ezt követően a megfelelő szolgáltatás metódusokhoz rendeltem a kívánt profilokat. . [Description("List of manufacturers.")] [WebGet(UriTemplate = "")] [AspNetCacheProfile("OneWeek")] public Manufacturers GetManufacturers() { . } . 42. ábra A gyártók listája
felkészítve a cache-elésre A fenti konfiguráció révén a válaszfejlécbe bekerült a Cache-Control private értékkel, jelezve, hogy csakis a kliensnél tárolható az erőforrás. Továbbá ugyanitt max-age érték és Expires header is bekerült. [42] 63 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben 6.23 A hitelesítés Az erőforrások csak hitelesített ügyfelek számára érhetők el. Az ügyfelek továbbá biztonsági szerepkörökkel és termékszűréssel is rendelkeznek az API kulcsuk révén. Tehát a szerveroldali tárazás egyáltalán nem tűnhet biztonságosnak, mivel ilyenkor hitelesítés már nem történik12. De az előző fejezetben említett Authorization fejléc szerinti tárazás ezt áthidalja, így API kulcsonként létrejönnek az erőforrások másolatai, amelyekhez csakis a szóban forgó kulccsal rendelkező kliensek férnek hozzá. A korábban említett ItemNumber nevű gyorsítótár profil nem vizsgálja az Authorization
fejlécet. Tehát minden ügyfél ugyanabból a forrásból táplálkozik, mivel a cikkszámok biztonsági szempontból alacsony érzékenységűek, ám fontos, hogy auto-complete esetén gyorsan hozzájuk lehessen férni. Szükséges volt tesztelnem, azt az esetet, ha API kulcsot nem adnak meg a kliensek. Ez természetesen 401, ami az egyszerűség kedvéért szintén cache-elve lett, hasonlóan a többi hibaüzenethez. A hibaüzenetek cache-elése is megfontolandó REST alkalmazások készítésekor, mivel így csökkenthetjük a szerveren keletkező kivételek számát. Ezt hívják negatív cache-elésnek [43]. 6.24 Terhelésteszt Miután bekonfiguráltam a gyorsítótárazást, terhelési tesztet hajtottam végre az API-n, ekkor már éles környezetben. A fejezetben talán a legköltségesebb erőforrás tesztjét mutatom be, mely a DetailedProductInformation-t célozza meg. A kliens web-alkalmazás legtöbbször ezt a szolgáltatást hívta más-más cikkszám paraméterrel az
URI-ben. A teszt anyag a szóban forgó vállalkozás terméktörzsében szereplő 20 legkelendőbb termék cikkszáma volt. http://api.com/WebService/DetailedProductInformation/ ?itemNumber=480204AA &forceItemNumberEquality=false &rowCount=100 &relationIDs=1,2,4,8,16,32 43. ábra Egy URI felparaméterezve A Visual Studio 2010-ben a Web Performance Test az Internet Explorer-rel nagyon könnyedén össze lett állítva. Ez a teszt 20 GET kérést tartalmazott más-más cikkszámokkal 12 Ha van a tárban egy olyan erőforrás, amely az URI-nek megfelel, akkor az IIS visszaadja azt, és nem hívja meg a web-alkalmazás kódját, ahol a hitelesítés van implementálva. 64 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben (43. ábra) Majd ezt követően egy Load Test-et hoztam létre, ahol 20 ügyfél bombázta kérésekkel a web-szolgáltatást, 5 percen keresztül, várakozás és bemelegítés nélkül, az imént összeállított URI-ket random
módon alkalmazva. Az eredmény mintavételezés 5s volt [44] Az első Load Test-nél kikapcsoltam a web-szolgáltatás cache-elését. Mindössze 924 kérés fért bele az 5 perces teszt-be és az átlagos válaszidő 5,4s volt (44. ábra) 44. ábra A válaszidők alakulása az első terheléstesztnél (cache nélkül) A második terhelésteszt előtt visszakapcsoltam a gyorsítótárazást, és újraindítottam az alkalmazáskészletet. A teszt paraméterei megegyeztek az előzővel (45 ábra) Mint látható, a web-kiszolgáló 1:45-re elmentette a kért erőforrásokat, és ezt követően az összes kérést ebből szolgálta ki. Ennél a tesztnél 3164 hívást szolgált ki a web-szolgáltatás 5 perc alatt, és az átlagos válaszidő 1,06s lett. 45. ábra A válaszidők alakulása a második terheléstesztnél (cache) Tehát mint látható, a REST szolgáltatásoknál nagyon fontos a cache-elés, továbbá ez nagy előny a SOAP szolgáltatásokkal szemben. 65 Pótári
Tamás RESTful web-szolgáltatások készítése WCF-ben 7. Összefoglalás A diplomamunkámban egy meglévő, Microsoft .NET alapú webes kereskedelmi rendszer RESTful web-szolgáltatásokkal történő kibővítését végeztem el, és ezen keresztül mutattam be a RESTful szolgáltatások fejlesztésének lehetőségeit WCF-ben. Az elméleti bevezetőt követően ismertettem a kivitelezés minden lépését, a REST architektúra stílus által kínált modellezési és tervezési folyamaton át a WCF alapú implementálásig. Továbbá szemléltettem az elkészült szolgáltatás sikeres tesztelését, beüzemelését és optimalizálását. A munkám során a legtöbb tevékenységnél a szakértők javaslatait illetve korábbi tapasztalataimat is felhasználtam, hogy végül egy kényelmes, remekül használható, karbantartható és optimális teljesítményű megoldást készítsek. Az elkészült szolgáltatás segítségével új lehetőségek elé néznek a kereskedelmi
rendszert használó vállalkozások. Képesek lesznek új, un „third-party” fejlesztésű rendszerekkel összekötni a sajátjukat, legyenek azok szerveroldali, kliensoldali – mint az okos telefonok – vagy hibrid megoldások. Így kiterjeszthetik az üzletüket és akár új üzletágakban is érdekeltté válhatnak. A munkám produktuma ezt követően is fejlődik, próbálja kielégíteni a kliensek újabb és újabb igényeit, mivel erre a célra hivatott létrejönni. 66 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben I. sz Melléklet A fejezetben egy ASP.NET-es példaalkalmazás látható Az alkalmazás egy egyszerű termékkeresőt tartalmaz, mely az elkészült web-szolgáltatást használja. 1. melléklet ábra A termékkereső oldal felépítése A 2. melléklet ábra mutatja a GetProducts() metódust, mely lekérdezi a termékeket és anonim típusú objektumok gyűjteményévé konvertálja azokat LINQ to XML-lel. public IEnumerable
GetProducts(string itemNumber, int manufacturerID, int pageSize, int startRowIndex, int maximumRows) { int pageNumber = startRowIndex / pageSize; using (HttpClient client = new HttpClient( new Uri(string.Format("http://localhost:8800/Product/?itemNumber={0}&manufacturerID={1}& rowCount={2}&pageNumber={3}", new object[] { itemNumber, manufacturerID, pageSize, pageNumber })))) { client.DefaultHeadersAdd("Authorization", "APIKey 1DE75149-0E06-4935-86A0-A7A52A9B4362"); using (var responseMessage = client.Get()) { var responseXml = XDocument.Load(responseMessageContentReadAsStream()); XNamespace priceNs = "urn:Prices-Schema"; return (from p in responseXml.Descendants("Product") select new { ItemNumber = p.Element("ItemNumber")Value, Name = p.Element("Name")Value, Manufacturer = p.Element("Manufacturer")Element("Name")Value, ListPrice = string.Format("{0} {1:000}",
p.Element("ListPrice")Element(priceNs + "Currency")Value, (decimal)p.Element("ListPrice")Element(priceNs + "Value")) }).ToList(); } } } 2. melléklet ábra A termékek listáját lekérdező metódus 67 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben Hasonlóan a jövőbeli kliensekhez, a web-szolgáltatás közvetlenül lett meghívva, és nem lettek használva az alaprendszer komponensei. A metódust a ProductsObjectDataSource hívja, és az adatforrást pedig a ProductsGridView használja. Az adatforrás és a táblázat is támogatja a lapozást, habár csak PagerSettings.Mode=PagerButtonsNextPrevious módban A 3 melléklet ábrán látható a termékkereső, ahol a táblázat mérete 5 sor és a láblécben lehetőség van a következő 5 sor lekérdezésére is. 3. melléklet ábra A termékkereső működés közben 68 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben
Irodalomjegyzék [1] S. R Leonard Richardson, „The Resource-Oriented Architecture,” in RESTful Web Services, Sebastopol, California, OReilly, 2007, pp. 79-105 [2] J. Flanders, „SOAP,” in RESTfulNET, Sebastopol, California, OReilly, 2009, p 4 [3] J. Flanders, „REST,” in RESTfulNET, Sebastopol, California, OReilly, 2009, p 5 [4] S. R Leonard Richardson, „Whats a resource,” in RESTful Web Services, Sebastopol, California, OReilly, 2007, p. 81 [5] S. Allamaraju, „Identifying Resources,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 29-44 [6] S. R Leonard Richardson, „URIs Should Be Descriptive,” in RESTful Web Services, Sebastopol, California, OReilly, 2007, p. 83 [7] S. Allamaraju, „Designing URIs,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 75-83 [8] J. Flanders, „Uniform Interface,” in RESTFulNET, Sebastopol, California, OReilly, 2009, pp. 7-9 [9] J. Papa, „Invoking the Service with
WebClient,” in Data-Driven Services with Silverlight 2, Sebastopol, California, OReilly, 2009, pp. 170-172 [10] S. R Leonard Richardson, „Representations,” in RESTful Web Services, Sebastopol, California, OReilly, 2007, pp. 91-93 [11] S. R Leonard Richardson, „Deciding Between Representations,” in RESTful Web Services, Sebastopol, California, OReilly, 2007, pp. 92-94 [12] S. R Leonard Richardson, „Links and Connectedness,” in RESTful Web Services, Sebastopol, California, OReilly, 2007, pp. 94-96 [13] S. Allamaraju, „When to Combine Resources into Composites,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 34-37 [14] S. Allamaraju, „When to Use Controllers to Operate on Resources,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 39-43 [15] S. Allamaraju, „How to Support Transactions,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 213-215 [16] J. Flanders, „Channels and
Dispatching,” in RESTfulNET, Sebastopol, California, OReilly, 2009, pp. 19-22 [17] J. Flanders, „Web Programming in WCF 35,” in RESTfulNET, Sebastopol, California, OReilly, 2009, pp. 27-33 [18] „WCF Web HTTP Service Help Page,” Microsoft, 25 június 2011. [Online] Available: http://msdn.microsoftcom/en-us/library/ee230442aspx [Hozzáférés dátuma: 5 szeptember 2011]. [19] „WCF Web HTTP Programming Model,” Microsoft, 25 június 2011. [Online] Available: http://msdn.microsoftcom/en-us/library/bb412169aspx [Hozzáférés dátuma: 5 szeptember 2011]. [20] B. A Joseph Albahari, „The Data Contract Serializer,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 612-622 [21] B. A Joseph Albahari, „Using the Serializers,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 614-617 [22] B. A Joseph Albahari, „Serializing Subclasses,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 617-618 [23] B. A Joseph
Albahari, „Object References,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 618-620 69 Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben [24] P. Tamás, „A sorosíthatóság,” in NET komponensorientált szoftverfejlesztés bemutatása egy menedzser szoftveren keresztül, Kecskemét, Kecskeméti Főiskola Gépipari és Automatizálási Műszaki Főiskolai Kar, 2007, pp. 34-36 [25] J. Löwy, „Egyéni szerializáció és verziókövetés,” in NET komponensek programozása, Budapest, Kossuth Kiadó (OReilly), 2004. [26] B. A Joseph Albahari, „Version Tolerance,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 620-621 [27] B. A Joseph Albahari, „Null and Empty Values,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, p. 622 [28] B. A Joseph Albahari, „Data Contracts and Collections,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 622-626 [29] B. A Joseph
Albahari, „Member Ordering,” in C# 40 in a Nutshell, Sebastopol, California, O’Reilly, 2010, p. 621 [30] J. Löwy, „Interfész alapú programozás,” in NET komponensek programozása, Budapest, Kossuth Kiadó (OReilly), 2004, pp. 55-76 [31] J. Löwy, „Rendszerbiztonsági tag alapú biztonság,” in NET komponensek programozása, Budapest, Kossuth Kiadó (OReilly), 2004, pp. 408-416 [32] „ASP.NET Routing,” Microsoft, [Online] Available: http://msdnmicrosoftcom/enus/library/cc668201aspx [Hozzáférés dátuma: 5 szeptember 2011] [33] S. Allamaraju, „How to Design Query Responses,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 140-142 [34] S. Allamaraju, „How to Assign Link Relation Types,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 93-95 [35] „Create a Custom Authorization Manager for a Service,” Microsoft, [Online]. Available: http://msdn.microsoftcom/en-us/library/ms731774aspx [Hozzáférés
dátuma: 6 szeptember 2011]. [36] J. Flanders, „Authorizing Endpoints,” in RESTfulNET, Sebastopol, California, OReilly, 2009, pp. 170-174 [37] J. Flanders, „Status Codes,” in RESTfulNET, Sebastopol, California, OReilly, 2009, pp 224-231. [38] J. Flanders, „201-Created,” in RESTfulNET, Sebastopol, California, OReilly, 2009, pp 227-229. [39] S. Allamaraju, „Link Relation Registry,” in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, p. 280 [40] B. K A K M W Mickey Gousset, „Web Performance Tests,” in Professional Application Lifecycle Management with Visual Studio® 2010, Indianapolis, IN 46256, Wiley Publishing, Inc. (Wrox), 2010, pp 278-297 [41] J. Flanders, „HttpContextCache,” in RESTfulNET, Sebastopol, California, OReilly, 2009, p. 241 [42] S. H D R Bill Evjen, „Output Caching,” in ASPNET 35 In C# and VB, Indianapolis, Indiana, Wiley Publishing, Inc. (Wrox), 2008, p 1071 [43] S. Allamaraju, „When To Set Expiration Caching Headers,”
in RESTful Web Services Cookbook, Sebastopol, California, OReilly, 2010, pp. 151-153 [44] B. K A K M W Mickey Gousset, „Load tests,” in Professional Application Lifecycle Management with Visual Studio® 2010, Indianapolis, IN 46256, Wiley Publishing, Inc. (Wrox), 2010, pp. 297-312 Ω 70