Mostanság nem nagyon szólalok meg, hiszen nemrég voltam so-called nyaralni, majd természetesen kiderült, hogy helyettem nem dolgozott senki. Aztán nagy titokban megint elkezdtem foglalkozni a világuralmat is célzó webes szoftveremmel, egyelőre még egyedül. Természetesen mint mindig, most is vannak igennagyon jó ötletek, amit szeretnék megosztani.
Most egyszerű HTML alappilléreket találtam, amiket érdemes (forrón ajánlott) használni, ha webes szoftvert írsz.
Probléma: adatokat módosító URL-t tárol el a felhasználó, és minden egyes Kedvencek-kattintás egy-egy hibás módosítást eredményez
A webes alkalmazást ketté kell osztani GET és POST hívásokra. A GET-es hívásokat megjelenítésre, a POST-os hívásokat módosításra kell használni.
Probléma: a POST kérések bekerülnek a böngésző listájába, és egy újratöltés vagy hátra-előre navigálás során újra megtörténik a módosítás.
A POST kérések nem valók a böngésző (history) listájába. Tulajdonképpen a POST kérések eredménye nem szabad, hogy direkt weboldal legyen: használjuk a PRG (POST-REDIRECT-GET) mintát, azaz a POST után irányítsuk a böngészőt egy GET-es oldalra.
A cikkben (és a forrásul felsorolt cikkekben) arról vitáznak, hogy miként is oldják meg ezt a problémát. Hiszen mi nem a POST kérés megismétlését akarjuk, hanem a POST feldolgozása után egy másik oldal megjelenítését (ami lehet megint a szerkesztő oldal). A végén megállapítják, hogy ha nem használunk régi Netscape-t (márpedig… ugye, senki sem használ?), akkor a legegyszerűbb, ha 303-as kóddal küldjük el a felhasználót a megjelenítő kódhoz (PHP példa):
header("Location: ".$url, false, 303);További érdekesség, hogy bár a HTTP/1.1 specifikáció szerint a Location fejlécben abszolút URL-t kell írni, minden további nélkül mennek a relatív URL-ek is. Egy problémám akadt csak: Safari-ban, ha az eredeti URI „xy.php?” volt, és a „Location” fejléc ? karakterrel kezdődő relatív URL volt, nos, akkor ahelyett, hogy a ? utáni részt kidobná, hozzáadja a relatív URL végéhez, így több kérdőjel lesz benne, valódi paraméter pedig egy sem. Ha van egy olyan szoftver, ami ha nincs megfelelően paraméterezve, hozzáírja a paramétert és újra hívja magát, nos az végtelen ciklusba kerül.
Probléma: Ha így visszaküldjük a megjelenítő oldalra a böngészőt, a gyorstár elavult adatokat tartalmazhat.
Webes szoftvernél nincs értelme gyorstárnak. Az oldalak itt nem holmi előre megtervezett prospektusok, hanem adatok rendszerezett megjelenítése. A pontos szövegezést, a képeket, vagy bármit nem határozhatjuk meg. A gyorstárnak itt nincs helye. Tessék kikapcsolni.
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Pragma: no-cache"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false);
Eredmény: a végén kezd kirajzolódni a Model-View-Controller komplex minta, amit már bizony elég régóta használnak a GUI (sőt, bármilyen UI) programozásban. Kicsit reszelni kell rajta, az igaz, de attól még MVC.
Most épp az MVC-t erősítem a szoftveremben, némileg eképpen:
Controller: Ezt – bonyolultsága okán – két felé szedtem: az egyik az oldalakat kezeli, a másik az oldalakon belül végrehajtható utasításokat. A végén csináltam egy Page és Action osztályt.
Page: ez az oldal egy rakás feladatot kapott. Megjegyzi, hogy ez melyik oldal, megtudja, hogy az oldalon milyen utasítások jöhetnek szóba. Bejelentkezteti és kilépteti a felhasználókat. Megjegyzi a felhasználók számára engedélyezett utasításokat, kezeli a menüt, tárolja az utasítások egymásba ágyazódását, meghatározza az elérhető al-utasításokat, és elérhetővé teszi azok URL-jét a View számára. Meghívja az utasításnak megfelelő Action-t, és a visszaadott View-t megjeleníti. Csinál még más View-okat is, a menünek, az előző oldalon előfordult hibáknak, hirdetésnek, bárminek, amit jól beágyaz az oldalt megjelenítő nézetbe. Azt hiszem, nem hagytam ki semmit, de ez nem biztos.
Action: ez már lényegesen egyszerűbb. Ellenőrzi a bejövő adatokat (Validate) Létrehozza a szükséges modelleket, felparaméterezi, végrehajtja a változtatásokat (ha kell), létrehozza a View objektumot, és átadja a kimenő paramétereket (akár a model objektumokat is, de ez csúnyának hangzik). Lehet, hogy ide kéne pakolni néhány Page-es dolgot, csak hogy meglegyen az egyensúly.
Model: Ezzel még nem tettem sok mindent, de az biztos, hogy nem fogom megbonyolítani az életemet: talán esetleg lesz minden modelhez egy Storage vagy DAO, vagy valami más tárolási és visszakeresési eljárás, de egyelőre nem hiszem, hogy hatalmas uniformizálást szeretnék.
View: No ezt is két felé kell szedni, hiszen az egyik a GET kéréseket, a másik a POST kérések utáni REDIRECT-et kell, hogy csinálja. Ez utóbbi egyszerűbb, de az első, a PageView már nem tűnik olyannak.
Előszöris, szükség van egy rakás változóra, és azok megjelenítésére. Például, ha van egy dátum mezőm (természetesen mindegyik UTC), mondjuk SQL formátumban, de a szoftvert valami tengerentúli országnak gyártom (pl. USA), akkor felmerül bennük a kérés, hogy ne év-hó-nap óra:perc:másodperc formátumban jelenjen meg, hanem nap/hó/év óra(12H):perc (AM/PM) formátumban, és persze legyek kedves más időzónát használni. Persze, kérnek dátum-idő választókat, ha a változót nem kiírni, hanem választani akarják. Erre csináltam egy változó (Variable) objektumot, ami egy változót többféleképpen tud megjeleníteni: szövegként, dátumként (megadható formátumban), rejtett, vagy más FORM elemként, listaként, választóként (select, checkbox-lista, vagy radio-lista). Természetesen mindet korrektül megjelenítve.
Aztán ott van a sablonok problémája. A megjelenítést érdemes teljesen külön kezelni az adatok feltöltésétől, tehát nem különálló View osztályokat kell csinálni, hanem az eredetit kell példányosítani, és megmondani, hogy hol is keresse a sablont. Az sem árt, hogy ha van egy különálló sablonkönyvtárunk is, csak az ügyfél számára, mert akkor nem kell feltúrni a szoftvert, mert valaki meg akarja változtatni egy szövegdoboz helyét.
Tovább megyek: a sablonokat kb. úgy képzeltük el hajdanán, hogy van egy fejléc, törzs, meg lábléc. A törzsöt piszkálta a szoftver, előtte betöltötte a fejlécet, ami betöltötte persze a törzs irányító madzagokat is, majd a végén betöltötte a láblécet, és kész. Hát ez aztán olyan egymásba ágyazott HTML kódokat eredményezett, amiből legtöbbször kimaradt egy
, vagy valami más őrület, hogy a fejlécben kezdődött egy HTML elem, a törzsben volt a tartalom, és a láblécben záródott be. Ennek nem sok teteje van, csináljuk fordítva: legyen egy (vagy több) általános sablon (mondjuk page-layout néven), ami megkapna néhány további sablon objektumot (pl. menu, ad, content, errors, ilyenek), és majd az oda rakja az al-sablonokat, ahova azt kell rakni. Tehát: beágyazás, egymás melletti darabka-farigcsálás helyett.
Ja, tényleg. A POST-REDIRECT-GET minta egyszerűbbé teszi azt az elképzelést, hogy a hibákat egyszerűen a következő GET-es oldalban jelenítsük meg. Ez mindannyiunk életét jelentősen megkönnyíti.
Ha valakinek további jó webes mintája van, ne habozzon, szóljon hozzá!