20 Webprogramozás a WebBroker és a WebSnap segítségével Az Internet, amely egyre nagyobb szerepet tölt be az életünkben, népszerûségét nagy részben a Világhálónak köszönheti, a Világháló pedig a HTTP protokollra épül. A 19. fejezetben megismerkedtünk a HTTP protokollal és az erre épülõ ügyfél és kiszolgáló oldali alkalmazásokkal. Mivel számos nagyteljesítményû, méretezhetõ és rugalmas kiszolgáló létezik, nincs igazán szükségünk arra, hogy saját alkalmazásokat fejlesszünk. A dinamikus webkiszolgáló alkalmazások általában programozhatók, így nincs szükség egyedi szoftver készítésére. Ezt a fejezetet teljes egészében a meglévõ kiszolgálókat kiegészítõ kiszolgáló oldali alkalmazások fejlesztésének szenteljük. Az elõzõ fejezet végén elkezdtünk ismerkedni a HTML oldalak dinamikus elõállításának módszereivel. Most azt fogjuk megnézni, hogyan építhetjük be ezeket egy kiszolgálóba. Ez a fejezet az elõzõ folytatása, de nem fedi le teljes egészében a könyv internetprogramozással kapcsolatos témaköreit; a 21. fejezetben a Delphi 7 IntraWeb megoldásával foglalkozunk, a 22. fejezetben pedig az XML szemszögébõl vizsgáljuk meg az internetprogramozást. Figyelem!
A fejezet egyes példáinak kipróbálásához szükségünk lesz egy webkiszolgálóra is. A legegyszerûbb megoldás a számítógépünkre egyébként is telepített Microsoft IIS vagy a Personal Web Server (Személyes webkiszolgáló) használata. Én ennek ellenére a nyílt forráskódú Apache Web Server (www.apache.org) használatát részesítem elõnyben. Nem fog sok idõt igénybe venni a kiszolgáló beállítása; a dokumentáció alapján gyorsan végezhetünk. A fejezet során a következõ témákkal foglalkozunk: • • • • •
Dinamikus weboldalak A WebBroker A webes hibakeresõ A WebSnap Csatolók és kiszolgáló oldali parancsnyelvek
958
Delphi 7 mesteri szinten
Dinamikus weboldal ak Amikor honlapokat böngészünk, jobbára statikus oldalakat (azaz HTML formátumú szövegfájlokat) töltünk le a webkiszolgálóról ügyfélgépünkre. Honlaptervezõként magunk is elkészíthetjük ezeket az oldalakat, a legtöbb esetben azonban a statikus lapokat egy adatbázis adatai alapján kell legyártanunk. Ezt a megközelítést alkalmazva pillanatképet kapunk az adatokról, ami jó megoldás, ha az adatok csak ritkán változnak. A témával a 19. fejezetben foglalkoztunk. A statikus HTML oldalak helyett használhatunk dinamikus oldalakat is. Ilyenkor közvetlenül az adatbázisból olvasunk be adatokat a felhasználó igényei szerint, programunk így mindig az aktuális adatokat jeleníti meg, nem pedig egy régebbi állapotot. Erre a módszerre természetesen akkor lehet szükség, ha adataink gyakran változnak. Korábban már említettük, hogy számos módszer áll a rendelkezésünkre ahhoz, hogy a webkiszolgáló viselkedését testreszabjuk; ugyanezekkel a módszerekkel dinamikusan változó HTML lapokat is készíthetünk. A webkiszolgálók programozásához használt két legismertebb módszer a CGI (Common Gateway Interface) és a webkiszolgálói API-k használata. Megjegyzés Fontos megjegyezni, hogy a Delphi 5 WebBroker megoldása (mely mind az Enterprise, mind a Professional kiadásban elérhetõ) elmossa a különbséget a CGI és a kiszolgáló API-k között, ugyanis egy közös keretrendszeren keresztül teszi elérhetõvé ezeket. Egy CGI programot tehát egyszerûen átalakíthatunk az ISAPI könyvtár használatára vagy beépíthetjük egy Apache kiszolgálóba.
A CGI áttekintése A CGI egy szabványos protokoll, melyet a böngészõ és a webkiszolgáló közti kapcsolattartásra használhatunk. Hatékonysága ugyan hagy némi kívánnivalót maga után, viszont széles körben elterjedt és nem rendszerfüggõ. A CGI lehetõvé teszi a böngészõ számára, hogy adatokat kérjen a kiszolgálótól, vagy éppen adatokat küldjön annak. Mûködésének alapjául az alkalmazások (ezek többnyire parancssori programok) parancssor-bemenete és -kimenete szolgál. Amikor a kiszolgáló lapkérést észlel a CGI program felé, elindítja azt, átadja a parancssor-bemenetet az alkalmazásnak, majd szabványos kimenetét visszaküldi az ügyfélgépre. A CGI alkalmazások készítéséhez számos eszközt és nyelvet használhatunk; közéjük tartozik a Delphi is. A nyilvánvaló korlátozások ellenére (a kiszolgálónak például Intel alapú Windows vagy Linux gépnek kell lennie) igen kifinomult CGI programokat írhatunk a Delphi vagy a Kylix segítségével. A CGI alacsonyszintû megoldás, mivel a kiszolgálóval való kapcsolattartásra a környezeti változók mellett a szabványos parancssori bemenetet és kimenetet használja.
20. fejezet • Webprogramozás a WebBroker és a WebSnap segítségével Ha CGI programot szeretnénk írni mindenféle támogató osztályok használata nélkül, akkor készítsünk egy parancssoros Delphi programot, távolítsuk el a szokásos projekt-forráskódot, majd tegyük a helyére a következõ utasításokat: program CgiDate; {$APPTYPE CONSOLE} uses SysUtils; begin writeln ('content-type: text/html'); writeln; writeln (''); writeln ('
Time at this site'); writeln (''); writeln ('
Time at this site
'); writeln ('
'); writeln ('
'); writeln (FormatDateTime('"Today is " dddd, mmmm d, yyyy,' + '"
and the time is" hh:mm:ss AM/PM', Now)); writeln ('
'); writeln ('
'); writeln ('
Page generated by CgiDate.exe'); writeln (''); end.
A CGI programok egy fejlécet hoznak létre a szabványos kimeneten, melyet HTML szöveg követ. Ha a fenti programot közvetlenül futtatjuk, a terminálablakban az egyszerû szöveget láthatjuk. Ha azonban egy webkiszolgálóról indítjuk el és a kimenetet a böngészõbe küldjük, a 20.1. ábrán látható formázott HTML szöveg jelenik meg.
20.1. ábra A CgiDate program kimenete a böngészõben.
959
960
Delphi 7 mesteri szinten
Ha összetettebb alkalmazásokat szeretnénk készíteni az egyszerû CGI segítségével, ezért komolyan meg kell dolgoznunk. Ha például a HTTP-kérelem állapotadatait szeretnénk elérni, hozzá kell jutnunk a megfelelõ környezeti változókhoz; ezt tesszük a következõ sorokban: // elérési út lekérdezése GetEnvironmentVariable ('PATH_INFO', PathName, sizeof (PathName));
Dinamikus könyvtárak használata Az elõzõektõl eltérõ megközelítést kínálnak a webkiszolgálói API-k, azaz a népszerû ISAPI (Internet Server API, melyet a Microsoft fejlesztett ki), valamint a kevésbé ismert NSAPI (Netscape Server API) használata. Ezek lehetõvé teszik olyan DLL írását, melyet a kiszolgálók saját címterükbe töltenek, és bizonyos ideig a memóriában tartanak. A DLL betöltése után a kiszolgáló a fõfolyamat szálaiként hajthatja végre az egyes kérelmeket, ahelyett, hogy minden egyes kérelem esetében a CGI-hez hasonlóan egy új EXE fájlt kellene elindítania. Amikor a kiszolgáló lapkérést kap, betölti a DLL-t (amennyiben ez még nem történt meg), majd elindítja a megfelelõ kódot, ami a lapkérést egy új szál létrehozásával vagy egy meglévõ szál felhasználásával dolgozza fel (az ISS webkiszolgáló támogatja a szálgyûjtést, így nem kell minden kérelemhez új szálat létrehozni). A DLL ezután visszaküldi a megfelelõ adatokat a lapot kérõ ügyfélnek. Mivel ez a mûvelet általában a memóriában történik, ezek az alkalmazások gyorsabbak, mint CGI-t használó társaik, így az adott rendszer több egyidejû lapkérés kezelésére is képessé válik.
A Delphi WebBroker megoldása A CGI bemutatására szolgáló fenti kódrészletek csak a protokoll és az API kezelésének egyszerû, közvetlen módját szemléltették. Ezen a szinten tovább is bõvíthetnénk õket, többre jutunk azonban, ha megismerkedünk a Delphi WebBroker megoldásával. Ez egy önálló osztályszerkezetet takar a VCL-en, illetve a CLX-en belül, amely a webes kiszolgáló oldali fejlesztés könnyítését szolgálja, valamint egy különleges, WebModule nevû adatmodultípust is tartalmaz. Az Enterprise Studio és a Delphi Professional kiadásai egyaránt tartalmazzák ezt a keretrendszert (szemben a WebSnap keretrendszerrel, ami csak az Enterprise Studio kiadásban érhetõ el). A WebBroker segítségével könnyedén hozzáláthatunk ISAPI vagy CGI alkalmazásunk elkészítéséhez. Ehhez a New Items (Új elemek) ablak elsõ lapján jelöljük meg a Web Server Application lehetõséget. A megjelenõ ablakban az ISAPI, CGI, Apache 1, illetve Apache 2 és a Web App Debugger modulok közül választhatunk:
20. fejezet • Webprogramozás a WebBroker és a WebSnap segítségével
A Delphi mindegyik esetben olyan projektet készít, amely WebModule-t tartalmaz. A WebModule egy nem látható komponens, amely az adatmodulhoz hasonlít. Ezt az egységet minden projekttípushoz használhatjuk, ilyenkor csak a fõ projektfájl változik. Egy CGI program esetében a projekt például így néz ki: program Project2; {$APPTYPE CONSOLE} uses WebBroker, CGIApp, Unit1 in 'Unit1.pas' {WebModule1: TWebModule}; {$R *.res} begin Application.Initialize; Application.CreateForm(TWebModule1, WebModule1); Application.Run; end.
Bár egy konzolos CGI programról van szó, a kód mégis egy szokványos Delphi alkalmazáshoz hasonlít. A titok nyitja, hogy a program által használt Application objektum nem a megszokott TApplication, hanem egy új osztály példánya. Ez az új osztály a TCGIApplication vagy más, a TWebApplication-bõl származó osztály leszármazottja. A legfontosabb mûveletek a webmodulban kapnak helyet. Ez a komponens a TCustomWebDispatcher osztályból származik; ez az osztály biztosítja a program ki- és bemenetét. A TCustomWebDispatcher osztály tartalmaz egy Request és egy Reply nevû tulajdonságot, melyek az ügyfél kérelme mellett azt a választ is tartalmazzák, amelyet a kérelem nyomán vissza kell küldeni. A két tulajdonság leírását egy-egy elvont alaposztály segíti (TWebRequest és TWebResponse), a programok azonban a megfelelõ objektumok (pél-
961
962
Delphi 7 mesteri szinten
dául a TISAPIRequest és a TISAPIResponse) segítségével készítik elõ azokat. Számunkra ezek az osztályok teszik elérhetõvé a kiszolgálóhoz érkezõ összes adatot. Ugyanez igaz a válaszokra is, amelyek kezelése szintén egyszerû feladat. A megoldás legnagyobb elõnye, hogy a WebBroker felhasználásával írt kód független az alkalmazás típusától (CGI, ISAPI, Apache); a kód így hordozható marad, nem kell megváltoztatnunk, ha máshol szeretnénk felhasználni. Ez a Delphi keretrendszer felépítése. Az alkalmazás kódjának megírásakor a WebModule Actions szerkesztõjét használva különféle mûveletsorozatokat határozhatunk meg (ezek az Actions tulajdonságba kerülnek) a kérés elérési útjától függõen:
Ez az útvonalnév a CGI vagy ISAPI program URL-jének része, ami a program neve után, de a paraméterek elõtt található; ilyen például a következõ URL path1 része: http://www.example.com/scripts/cgitest.exe/path1?param1=date
A különféle mûveletek megadásával a program egyszeûen válaszolhat a különbözõ elérési úton érkezõ kérelmekre, és minden lehetséges elérési úthoz más-más elõállító komponenst, illetve más-más OnAction eseménykezelõt rendelhetünk. Ha a kérelmeket általánosságban szeretnénk kezelni, az elérési utat természetesen ki is hagyhatjuk. Sõt, a webmodul helyett a programot egy sima adatmodulra is építhetjük, ha egy WebDispatcher komponenst rendelünk hozzá. Ha egy már meglevõ Delphi alkalmazást kívánunk webkiszolgálói bõvítménnyé alakítani, ez a helyes megközelítés. Figyelem!
A WebModule a WebDispatcher alaposztály leszármazottja, így nincs szükség arra különálló komponensként. A WebSnap programokkal ellentétben a WebBroker alkalmazások nem rendelkezhetnek több továbbítóval (diszpécserrel) vagy webmodullal. Jegyezzük meg azt is, hogy a WebDispatcher mûveleteinek semmi közük nincs az ActionList vagy az ActionManager komponens mûveleteihez. Amikor létrehozzuk az alkalmazást indító HTML lapokat, a hivatkozások lapkéréseket küldenek az elérési utak URL-jeire. A felhasznált könyvtárnak köszönhetõen, amely a paramétertõl (esetünkben ez az útvonalnév) függõen különbözõ mûveletek elvégzésére alkalmas, a kiszolgáló képes gyorsabban válaszolni a felhasználók kéréseire. Ugyanez részben igaz a CGI alkalmazásokra is; a kiszolgáló ilyenkor több példányban fut ugyan, de a fájl gyorstárazásával növelhetõ az elérés sebessége.
20. fejezet • Webprogramozás a WebBroker és a WebSnap segítségével Az OnAction esemény kezelésénél meghatározhatjuk a kérésre adandó választ; ezek az eseménykezelõ legfontosabb paraméterei. Nézzünk meg egy egyszerû példát: procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := '
Hello Page' + '
Hello
' + '
Page generated by Marco
'; end;
A Response paraméter Content tulajdonságába írhatjuk be azt a HTML kódot, amit a felhasználó látni fog. A kód egyetlen hátulütõje, hogy noha a kimenet a böngészõben helyesen (több sorban) jelenik meg, a HTML forráskódban a karakterlánchoz egyetlen sor tartozik. Ha szeretnénk a magunk számára olvashatóbbá tenni a szöveget, a #13 (újsor karakter) beírásának segítségével több sorba tördelhetjük azt. Annak érdekében, hogy más mûveletek is kezelhessék a kérelmet, az utolsó, Handled nevû paraméter értékét False-ra (hamis) állítjuk. Ha meghagyjuk az alapértelmezett True értéket, a kérelem feldolgozása után a webmodul befejezettnek tekinti a mûveletet. A legtöbb webes alkalmazás kódja a WebModule tárolóban meghatározott mûveletek OnAction eseménykezelõibõl áll. Ezek a mûveletek kérelmeket fogadnak az ügyfelektõl, majd válaszolnak ezekre (a Request és Response paramétereken keresztül). Amikor elõállító komponenseket (producer) használunk, az OnAction esemény gyakran egyszerûen a komponens tartalmának megfelelõ Response.Content hozzárendeléssel tér vissza. A kód végrehajtását megkerülhetjük, ha az elõállító komponenst a mûvelet Producer tulajdonságához rendeljük, így az elõbbi egyszerû eseménykezelõk megírásával nem kell többé bajlódnunk. Tipp
A Producer helyett használhatjuk a ProducerContent tulajdonságot is. Ez a tulajdonság lehetõvé teszi, hogy olyan egyedi osztályokkal dolgozzunk, amelyek nem a TCustomContentProducer osztály leszármazottai, de megvalósítják az IProduceContent felületet. A ProducerContent tulajdonság majdnem olyan, mint egy felület típusú tulajdonság: ugyanúgy viselkedik, ez a viselkedés azonban a tulajdonságszerkesztõ következménye, és nem a Delphi felülettulajdonság-támogatására épül.
Hibakeresés a Web App Debugger segítségével A webes Delphi alkalmazások hibáinak felderítése idõnként nehéz feladat. Nem lehet egyszerûen elindítani a programot, és töréspontokat helyezni el benne; a webkiszolgálót kell rávennünk arra, hogy a Delphi hibakeresõjében futtassa a CGI programot vagy
963
964
Delphi 7 mesteri szinten
könyvtárat. A gazdaalkalmazást a Delphi Run Parameters (Futási paraméterek) ablakában is megadhatjuk, erre azonban csak akkor van lehetõségünk, ha a Delphi futtatja a webkiszolgálót (ami gyakran egy Windows-szolgáltatás, nem pedig önálló program). A probléma megoldására a Borland kifejlesztett egy webes alkalmazások hibakeresésére szolgáló programot (Web App Debugger). Ez a program, amelyet a Tools menü megfelelõ elemének kiválasztásával indíthatunk el, egy webkiszolgáló, amely az általunk megadott kapunál (alapértelmezés szerint az 1024-esnél) várja a kéréseket. Amikor kérés érkezik, a program továbbítja azt egy önálló alkalmazásnak. A Delphi 6 ehhez COM megoldásokat használt; a Delphi 7 ezzel szemben az Indy foglalataira (socket, kapcsolódási pont) támaszkodik. A webkiszolgáló alkalmazást mindkét esetben a Delphi fejlesztõkörnyezetén belül futtathatjuk, beállíthatjuk a szükséges töréspontokat, majd (miután elindítottuk a programot a Web App Debugger segítségével) ugyanúgy kereshetjük a hibákat, mint egy hagyományos program esetében. A Web App Debugger kiváló naplót készít a beérkezõ kérésekrõl és a böngészõhöz továbbított válaszokról. A programnak van egy Statistics (Statisztika) lapja is, ahol az egyes kérések teljesítéséhez szükséges idõket vehetjük szemügyre, így tesztelhetjük programunk hatékonyságát különbözõ körülmények között. A Delphi 7 Web App Debuggerének másik újdonsága, hogy a program a korábbi VCL helyett a CLX könyvtárra épül. A felhasználói felület megváltozása és a foglalatok használatára való áttérés egyaránt azt a célt szolgálják, hogy a Web App Debugger a Kylixban is használható legyen. Figyelem!
Mivel a Web App Debugger Indy foglalatokat használ, a program gyakran kap majd EidConnClosedGracefully kivételeket. A Delphi 7 projektek ezért ezt a kivételt alapértelmezés szerint figyelmen kívül hagyják. A New Web Server Application ablak megfelelõ lehetõségének megjelölésével könnyedén létrehozhatunk olyan alkalmazásokat, amelyek képesek együttmûködni a hibakeresõvel. Egy szabványos projektrõl van szó, amely egy fõ formból és egy webmodulból áll. A form tartalmazza azt a kódot, amely elvégzi a kezdeti beállításokat, és bejegyzi a programot a Windows rendszerleíró adatbázisába: initialization TWebAppSockObjectFactory.Create('program_neve');
A Web App Debugger ezeknek az adatoknak a felhasználásával fér hozzá az elérhetõ programok listájához. Ezt teszi, amikor a hibakeresõben az alapértelmezett URL-t használjuk (lásd 20.2. ábra). A lista tartalmazza az összes bejegyzett kiszolgálót, nem csak azt, amelyiket éppen futtatjuk, és a segítségével el is indíthatjuk ezeket. Ez egyébként nem tanácsos, mivel a programot a Delphi fejlesztõkörnyezetében kell futtatnunk ahhoz, hogy hibákat kereshessünk benne. (A View Details gombra kattintva kinyithatjuk a listát; ebben a nézetben a futtatható fájlok listája mellett sok más részlet is megjelenik.)