1 Szegedi Tudományegyetem Informatikai Tanszékcsoport Diplomamunka Készítette: Pótári Tamás Programtervező informatikus MSc Témavezető: Dr. Alexin Zol...
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, ASP.NET 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.4.1. Az erőforrások meghatározása ................................................................................................... 10 2.4.2. Az URI-k megtervezése ............................................................................................................... 11 2.4.3. Az egységes interfész implementálása ........................................................................................ 12 2.4.4. A reprezentáció megtervezése .................................................................................................... 13 2.4.5. Kapcsolatok kialakítása .............................................................................................................. 15 2.5. TERVEZÉSI MINTÁK, SPECIÁLIS MEGOLDÁSOK .................................................................................... 15 2.5.1. 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.2.1. A WCF WebHttp szolgáltatások adottságai fejlesztői szempontból............................................ 19 3.3. A DATA CONTRACT SOROSÍTÁS .......................................................................................................... 20 3.3.1. Deklaratív programozás ............................................................................................................. 21 3.3.2. Öröklés és interfészek implementációi........................................................................................ 22 3.3.3. Hivatkozások és körkörös hivatkozások...................................................................................... 22 3.3.4. Verzió tolerancia ........................................................................................................................ 22 3.3.5. Null és üres értékek .................................................................................................................... 23 3.3.6. Gyűjtemények ............................................................................................................................. 24 3.3.7. 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.2.1. Interfészek és implementációik .................................................................................................. 29 4.2.2. Komponens alapú biztonság ....................................................................................................... 30 4.2.3. A lokalizáció ............................................................................................................................... 31 4.2.4. 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.2.1. Az érintett témakörök .................................................................................................................. 34 5.2.2. Kompozitok ................................................................................................................................. 36 5.2.3. A közös interfész ......................................................................................................................... 38 5.3. AZ URI-K MEGTERVEZÉSE .................................................................................................................. 38 5.3.1. Az URI template-ek megtervezése ............................................................................................... 39 5.3.2. 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.5.1. A Data Contract dekoráció és részleges objektumok ................................................................. 44 5.5.2. A kívánt reprezentáció megadása ............................................................................................... 45 5.5.3. Nagyobb gyűjtemény jellegű erőforrások lapozhatósága ........................................................... 46 5.6. ÖSSZEKAPCSOLHATÓSÁG.................................................................................................................... 47 5.6.1. A Link-ek inicializálása .............................................................................................................. 48 5.6.2. Link relációk ............................................................................................................................... 50 5.7. HITELESÍTÉS ....................................................................................................................................... 51 5.7.1. 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.8.1. Globális hibakezelés ................................................................................................................... 52 5.8.2. A 201 státusz kód ........................................................................................................................ 53 5.9. DOKUMENTÁCIÓ ................................................................................................................................. 54 5.9.1. Help Link-ek az erőforrásokban ................................................................................................. 55 5.10. TESZTELÉS ........................................................................................................................................ 56 5.10.1. Egységtesztek ............................................................................................................................ 56 5.10.2. Teszteredmények ....................................................................................................................... 57 5.10.4. 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.1.1. Kialakítás és kommunikáció ....................................................................................................... 60 6.2. GYORSÍTÓTÁRAZÁS ............................................................................................................................ 61 6.2.1. A átmenetileg tárolható erőforrások meghatározása ................................................................. 62 6.2.2. A cache-elés beállítása ............................................................................................................... 62 6.2.3. A hitelesítés................................................................................................................................. 64 6.2.4. 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.4.1. 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.4.2. 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.wikipedia.org/wiki/Representational_State_Transfer (REST a wiki-n)
•
http://www.kereskedelmirendszer.hu/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.4.3. 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.4.4. 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.kereskedelmirendszer.hu/Rendeles/223.xml (A 223-as rendelés XML alakban)
•
http://www.kereskedelmirendszer.hu/Rendeles/223.json (A 223-as rendelés JSON alakban)
•
http://www.kereskedelmirendszer.hu/Rendeles/223.xhtml (A 223-as rendelés XHTML alakban)
A nyelvi kultúrák esetén például: •
http://www.documents.com/hu/documents/101 (A 101-es dokumentum magyarul)
•
http://www.documents.com/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.4.5. 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.5.1. 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.ServiceModel.Channels.Message 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 3.0-ban csak erre volt lehetőség [16].
3.2. A WebHttp szolgáltatások A System.ServiceModel.Web.dll szerelvény a 3.5-ö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 System.ServiceModel és a System.ServiceModel.Web 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.2.1. 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.IO.Stream 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.3.1. 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.Runtime.Serialization 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.3.2. Ö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.3.3. 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.3.4. 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.3.5. 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.3.6. 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í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í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.3.7. 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 DataMember.Order, 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> <serviceBehaviors>
/>
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 2.0 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 4.0-á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.2.1. 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.2.2. 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.Product.Save() 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.2.3. 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.2.4. 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 ASP.NET-
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.Collections.Generic.IList-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, Products.Manufacturer 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.2.1. 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 Collections.Manufacturers gyűjtemény A Collections.Manufacturers (11. ábra) zárt System.Collections.Generics.List 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.Manufacturer.GetManufacturers());
12. ábra A Collections.Manufacturers 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 Collections.ItemNumbers 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 Collections.ItemNumbers 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.2.2. 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.2.3. 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 CommonTypes.WebResource.IWebResource é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.3.1. 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.3.2. 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.ServiceModel.Web.WebGetAttribute-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 ASP.NET-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.
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 ISavingServices.IsNew 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.Manufacturer.GetManufacturers()); } [Description("A manufacturer.")] [WebGet(UriTemplate = "/{id}")] public Products.Manufacturer GetManufacturer(string id) { return new Products.Manufacturer(int.Parse(id)); } [Description("Creates a new manufacturer.")] [WebInvoke(Method="POST", UriTemplate="")] public Products.Manufacturer CreateManufacturer(Products.Manufacturer m) { m.IsNew = true; m.Save(); return m; } [Description("Updates a manufacturer.")] [WebInvoke(Method = "PUT", UriTemplate = "/{id}")] public Products.Manufacturer UpdateManufacturer(string id, Products.Manufacturer m) { m.IsNew = false; m.ID = int.Parse(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.5.1. 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.WebResource.IWebResource.IsPartial 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. Original products2truefalseOriginal
... false00042307012truetrueOriginal ...
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 Products.ProductState 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.ISavingServices.IsNew tulajdonságait rendre kihagytam az erőforrásokból a System.Runtime.Serialization.IgnoreDataMemberAttribute-tal. Csak tulajdonságok lettek DataMemberAttribute-tal ellátva.
5.5.2. 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 ASP.NET kompatibilitás engedélyezve van a szolgáltatásban, ezért a System.Web.HttpContext 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=0.5”. Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(HttpContext.Current.Request.UserLanguages[0]);
23. ábra A nyelvi kultúra beállítása a megadott nyelvi paraméterrel A
Pótári Tamás RESTful web-szolgáltatások készítése WCF-ben
5.5.3. 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: •
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
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.WebResource.Link8 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.6.1. 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.WebResource.LinkProvider 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. /// [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.Current.CreateLinks(this); } } ... } }
29. ábra A Link-ek beállítása a Products.Manufacturer.ID set-ben A
web-szolgáltatás
LinkProvider-e
a
Global.asax-ban
lesz
beállítva
a
CommonTypes.WebResource.LinkProvider.Current-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.Current.Host.BaseAddresses[0].AbsoluteUri.Replace( OperationContext.Current.Host.BaseAddresses[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.6.2. 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 Products.ProductImage, 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 ... 0000010504.jpg10504truefalseselfhttp://localhost/WebService/ProductImage/10504imagehttp://localhost/Ps/0000010504.jpgthumbnailhttp://localhost/Ps/0000010504_tn.jpg0000010504_tn.jpg
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.7.1. 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 SystemComponents.APIKeyServiceAuthorizationManager. Ezt úgy helyeztem el a konfigurációban, mely minden service-re érvényes legyen.
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.ServiceModel.Web.WebFaultException-t
System.ServiceModel.Web.WebFaultException-t dobni státuszkóddal. Az utóbbihoz sorosítható objektumot is csatolhatunk, ami a response-ba belekerül.
5.8.1. 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.ServiceModel.Dispatcher.IErrorHandler 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 System.ServiceModel.Description.IServiceBehavior 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 System.ServiceModel.Web.WebFaultException kivételeket változatlanul továbbítja a kezelő.
5.8.2. 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
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(Products.Manufacturer m) { m.IsNew = true; m.Save(); if (m.Links != null && m.Links.Count > 0) { var uri=m.Links.FirstOrDefault(l => l.Relation == "self"); if(uri!=null) WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(new Uri(uri.Uri)); } 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.ComponentModell.DescriptionAttribute 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 Help.aspx 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 Help.aspx a dokumentáció főoldala 5.9.1. 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). ... 10851selfhttp://localhost:8800/Product/10851helphttp://localhost:8800/Product/help ...
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.10.1. 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.codeplex.com/ Dolgozatom írásakor az 1.2-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.10.2. 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.10.4. 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ő System.DateTime.MinValue é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.DateTime.MinValue.ToUniversalTime(). 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.1.1. 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ésekkel11. 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 ASP.NET kompatibilitás mód engedélyezése játszott közre. Így nem csak a routing hanem a professzionális ASP.NET 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.2.1. 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.2.2. 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> ...
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.2.3. 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.2.4. 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.
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.DefaultHeaders.Add("Authorization", "APIKey 1DE75149-0E06-4935-86A0-A7A52A9B4362"); using (var responseMessage = client.Get()) { var responseXml = XDocument.Load(responseMessage.Content.ReadAsStream()); 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:0.00}", 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=PagerButtons.NextPrevious 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, O'Reilly, 2007, pp. 79-105. [2] J. Flanders, „SOAP,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, p. 4. [3] J. Flanders, „REST,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, p. 5. [4] S. R. Leonard Richardson, „What's a resource,” in RESTful Web Services, Sebastopol, California, O'Reilly, 2007, p. 81. [5] S. Allamaraju, „Identifying Resources,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 29-44. [6] S. R. Leonard Richardson, „URIs Should Be Descriptive,” in RESTful Web Services, Sebastopol, California, O'Reilly, 2007, p. 83. [7] S. Allamaraju, „Designing URIs,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 75-83. [8] J. Flanders, „Uniform Interface,” in RESTFul.NET, Sebastopol, California, O'Reilly, 2009, pp. 7-9. [9] J. Papa, „Invoking the Service with WebClient,” in Data-Driven Services with Silverlight 2, Sebastopol, California, O'Reilly, 2009, pp. 170-172. [10] S. R. Leonard Richardson, „Representations,” in RESTful Web Services, Sebastopol, California, O'Reilly, 2007, pp. 91-93. [11] S. R. Leonard Richardson, „Deciding Between Representations,” in RESTful Web Services, Sebastopol, California, O'Reilly, 2007, pp. 92-94. [12] S. R. Leonard Richardson, „Links and Connectedness,” in RESTful Web Services, Sebastopol, California, O'Reilly, 2007, pp. 94-96. [13] S. Allamaraju, „When to Combine Resources into Composites,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 34-37. [14] S. Allamaraju, „When to Use Controllers to Operate on Resources,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 39-43. [15] S. Allamaraju, „How to Support Transactions,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 213-215. [16] J. Flanders, „Channels and Dispatching,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, pp. 19-22. [17] J. Flanders, „Web Programming in WCF 3.5,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, pp. 27-33. [18] „WCF Web HTTP Service Help Page,” Microsoft, 25 június 2011. [Online]. Available: http://msdn.microsoft.com/en-us/library/ee230442.aspx. [Hozzáférés dátuma: 5 szeptember 2011]. [19] „WCF Web HTTP Programming Model,” Microsoft, 25 június 2011. [Online]. Available: http://msdn.microsoft.com/en-us/library/bb412169.aspx. [Hozzáférés dátuma: 5 szeptember 2011]. [20] B. A. Joseph Albahari, „The Data Contract Serializer,” in C# 4.0 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 612-622. [21] B. A. Joseph Albahari, „Using the Serializers,” in C# 4.0 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 614-617. [22] B. A. Joseph Albahari, „Serializing Subclasses,” in C# 4.0 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 617-618. [23] B. A. Joseph Albahari, „Object References,” in C# 4.0 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ó (O'Reilly), 2004. [26] B. A. Joseph Albahari, „Version Tolerance,” in C# 4.0 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 620-621. [27] B. A. Joseph Albahari, „Null and Empty Values,” in C# 4.0 in a Nutshell, Sebastopol, California, O’Reilly, 2010, p. 622. [28] B. A. Joseph Albahari, „Data Contracts and Collections,” in C# 4.0 in a Nutshell, Sebastopol, California, O’Reilly, 2010, pp. 622-626. [29] B. A. Joseph Albahari, „Member Ordering,” in C# 4.0 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ó (O'Reilly), 2004, pp. 55-76. [31] J. Löwy, „Rendszerbiztonsági tag alapú biztonság,” in .NET komponensek programozása, Budapest, Kossuth Kiadó (O'Reilly), 2004, pp. 408-416. [32] „ASP.NET Routing,” Microsoft, [Online]. Available: http://msdn.microsoft.com/enus/library/cc668201.aspx. [Hozzáférés dátuma: 5 szeptember 2011]. [33] S. Allamaraju, „How to Design Query Responses,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 140-142. [34] S. Allamaraju, „How to Assign Link Relation Types,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 2010, pp. 93-95. [35] „Create a Custom Authorization Manager for a Service,” Microsoft, [Online]. Available: http://msdn.microsoft.com/en-us/library/ms731774.aspx. [Hozzáférés dátuma: 6 szeptember 2011]. [36] J. Flanders, „Authorizing Endpoints,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, pp. 170-174. [37] J. Flanders, „Status Codes,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, pp. 224-231. [38] J. Flanders, „201-Created,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, pp. 227-229. [39] S. Allamaraju, „Link Relation Registry,” in RESTful Web Services Cookbook, Sebastopol, California, O'Reilly, 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, „HttpContext.Cache,” in RESTful.NET, Sebastopol, California, O'Reilly, 2009, p. 241. [42] S. H. D. R. Bill Evjen, „Output Caching,” in ASP.NET 3.5 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, O'Reilly, 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.