Monthly Archives: February 2007

Saját DataSource paraméter

Épp amikor belefogtam volna, hogy megírjam, hogyan készítünk saját paraméter osztályt, amit aztán a data source osztályainkkal használhatunk, megérkezett a NetAcademia új tippek-trükkök hírlevele, amiben Tocsi lelőtte előlem a lényeget.

Ezt persze értékelhettem volna úgy, mint A Sors Kezét, amely nem akarja, hogy ez a téma megjelenjen a blogomban, de nem teszem, hanem inkább leírom, hogy mi az MSDN Kompetencia Központban hogyan oldjuk meg ezt a problémát.

Tocsinak alapvetően teljesen igaza van, a kulcs az, hogy származtatnunk kell a System.Web.UI.WebControls.Parameter osztályból, hiszen ebből származik a Control-, Cookie-, Form-, Profile-, QueryString- és SessionParameter osztály is.

Mi azt a megközelítést választottuk, hogy olyan általános osztályt hoztunk létre, amely a MembershipUser osztály tetszőleges tulajdonságát át tudja adni paraméterként. Ehhez természetesen definiáltunk az osztályon belül egy string típusú PropertyName tulajdonságot.

A következő lépésben felvérteztük magunkat egy rakás konstruktorral, követve az ősosztály konstruktorainak paraméterezését és felülírtuk a Clone() metódust is.

Az utolsó lépés, hogy új írtuk meg az Evaluate metódust, hogy az a MembershipUser osztálynak azt a tulajdonságát adja vissza, amit a PropertyName tulajdonság meghatároz. Ezt többféleképpen meg lehet fogalmazni, talán az a legegyszerűbb, amit a Microsoft fejlesztői a ProfileParameter.Evaluate() metódusban használtak (éljen a Reflector). Ha nekik jó, nekünk is jó alapon követtük a mintát és mi is a DataBinder.Eval() metódust használtuk:

    protected override object Evaluate( HttpContext context, Control control )
    {
        if( ( context != null ) && ( context.User.Identity.IsAuthenticated ) )
        {
            return DataBinder.Eval( Membership.GetUser(), this.PropertyName );
        }
        return null;
    }

Ezek után nem marad más hátra, mint regisztrálni az osztályt az oldalon és használni a data source vezérlő paraméterei között. Ha például a felhasználó egyedi azonosítóját szeretnénk átadni, akkor a PropertyName legyen "ProviderUserKey".

Vigyázat! Sajnos a Visual Studio data source konfigurációs varázslói nem ismerik fel, ezért időnként szó nélkül kitörlik ezt a paramétert! Mi ezért a biztonság kedvéért szerver oldali kommentben oda szoktuk másolni ezt a tag-et a data source varázsló közelébe.

Akit érdekel az általunk használt osztály teljes forráskódja, írjon ide egy kommentet és szívesen megosztom!

A forráskód letölthető innen: http://www.devportal.hu/Portal/Detailed.aspx?NewsId=a4a48992-8497-4db0-adc9-72435199ed62

 

Technorati tags:

SQL 2005 SP2

Valószínűleg mindenki találkozott már a hírrel, hogy az SQL Server 2005 második szervízcsomagja megjelent és talán a letöltő oldalon is járt már. Van azonban néhány olyan információ, ami úgy vettem észre, hogy nincs a köztudatban:

  • Ez a javítócsomag erősen ajánlott az SQL Server 2005 (Express változatok!) Vistán történő futtatásához. Innentől kezdve azonban az operációs rendszer rendszergazdái nem kapnak automatikusan sa jogot az SQL Serverben. Ahol ez probléma, ott használható a User Provisioning Tool, vagy akár parancssorból is – ez utóbbi főleg Express Edition esetén lehet érdekes.
  • Az SP2 telepítője nem frissíti a Books Online-t, tehát annak a telepítőjét külön kell letölteni a Microsoft Download Centerből – érdemes!
  • Szintén fejlesztésnél jöhet jól, ha tudjuk, hogy a példák és a példa adatbázisok is frissültek, azokat is külön lehet letölteni.
  • Létezik az SQL Serverhez egy ún. Feature Pack, amiből szerintem a leghasznosabb, hogy az SQLCMD önállóan is letölthető, illetve a Compact Edition is ide tartozik, továbbá ennek részeként találhatjuk meg a VWD Express Reporting add-inját is. A Feature Packből is van SP2, 2007. februári álnévvel, ez is külön tölthető le.
  • WSS v3 és MOSS 2007 rajongók figyelem, van új Reporting Services add-in.

Jó töltögetést!

Rekordok véletlen sorrendben

A kollégák megjegyezték, hogy időnként túl hosszúak a blog bejegyzéseim, ezért íme egy rövid (csak az ő kedvükért). A feladvány:

Adott egy Numbers tábla, benne egy ID nevű int típusú mező. Hogyan lehet T-SQL-ben legegyszerűbben a) visszakérni a rekordokat véletlenszerű sorrendben és b) véletlenszerűen kiválasztani egy rekordot?

Egy lehetséges működő megoldás az a) kérdésre:

    SELECT ID FROM Numbers ORDER BY NewID()

A b)-re pedig mindenki tudja innentől a választ, hiszen az csak egy TOP 1-gyel több. És hogy mi történik a háttérben, azt persze megtudhatjuk a végrehajtási tervből:

 

Technorati tags:

CardSpace és ASP.NET AJAX 3: Adatok bekérése és elküldése

(Aki esetleg lemaradt volna a bevezetőről, ezen a címen találja meg.)

Hozzunk létre Visual Studioban egy új ASP.NET AJAX-Enabled Web Site-ot, méghozzá IIS-en http-n kereszül, nem pedig File System locationnel. Ehhez rendszergazdai jogokkal kell indítani a Studiot! Ha később szeretnénk debuggolni, akkor az Internet Information Services MMC snap-inben engedélyezzük a Windows Authenticationt az így létrehozott mappánkra.

A kártya adatainak bekérése nem ördöngősség, mindössze egy OBJECT tag kell hozzá, amiben megadjuk az ún. claimeket, azaz tudatjuk a klienssel és a felhasználóval, hogy milyen adatokat várunk tőle, valahogy így:

<object name="xmlToken" id="objIdentitySelector" style="visibility:hidden;">
  <param name="tokenType" 
         value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" />
  <param name="requiredClaims" 
         value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname 
                http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname 
                http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress 
                http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" />
</object>

A sok claim közül a legfontosabb a privatepersonalidentifier, ez az a kliens oldali azonosító, amire vadászunk. A visibility stílust azért állítottam hiddenre, hogy ne jelenjen meg a böngészőben kis piros kocka az OBJECT tag helyén és ne is foglaljon helyet.

Ezen kívül az OBJECT tag TYPE attribútumát application/x-informationcard értékre kell állítanunk, de ezt inkább JavaScriptből fogjuk megtenni. Az objektum akkor jeleníteni meg a kártya bekérő ablakot, ha valaki hozzáfér a value tulajdonságához, akkor viszont azonnal és mindig. Ez elég kellemetlen, hiszen az oldal elpostázásakor a value értéke mindenképpen kiolvasódik, ami nagyon zavaró, hiszen egyáltalán nem biztos, hogy emiatt történik postback. Gondoljunk csak arra, hogy ezt a funkcionalitást a login oldalon fogjuk elhelyezni, a már működő Login kontroll alternatívájaként. Az alapértelmezett viselkedés az lenne, hogy amikor a felhasználó megadja a nevét és a jelszavát és rákattint a bejelentkezés gombra, még a rendszer az arcába tolja a kártya kiválasztó (ún. Identity Selector) ablakot. Ezen nekem csak úgy sikerült felülkerekednem, hogy a viselkedést meghatározó TYPE attribútumot akkor állítjuk be, amikor szükséges.

Tegyünk az oldalra egy linket, ami majd meghívja a szükséges JavaScriptet:

    <a href="javascript: selectInfoCard();">Kártya kiválasztása</a>

A meghívott függvényben az a feladatunk, hogy egy rejtett mezőbe átmásoljuk az Identity Selector által visszaadott XML-t, ugyanis a rejtett mező tartalmát sokkal könnyebben tudjuk elérni szerver oldalon, mint az OBJECT tartalmát. Ehhez persze kell egy rejtett mező, amiben az ún. XML Token fog utazni:

    <input id="hidIdentityToken" type="hidden" runat="server" />

A rejtett mező és az objektum elérését legegyszerűbben az ASP.NET AJAX-ban megjelenő $get függvénnyel tehetjük meg:

    <script language="javascript" type="text/javascript">
        function selectInfoCard()
        {
            var oIS = $get( "objIdentitySelector" );
            var hIT = $get( "hidIdentityToken" );
            oIS.type="application/x-informationcard";
            hIT.value = oIS.value;
            $get( "form1" ).submit();    
        }
    </script>

Ezzel azért van legalább 2 probléma:

Az első, hogy ha mindezt egy kontrollban valósítjuk meg, akkor az objektumok elérése nem lesz ilyen egyszerű, a rejtett mező szerver kontroll lévén más azonosítót fog kapni. Ezt persze szerver oldalon le tudjuk kérdezni és ki tudjuk vezetni egy property-be:

    protected string HiddenFieldClientName
    {
        get
        {
            return this.hidIdentityToken.Name;
        }
    }

A property-t pedig be tudjuk drótozni a JavaScriptbe és a korábbi második sort kicserélhetjük erre:

    var hIT = $get( "<%= HiddenFieldClientName %>" );

A második problémát szintén az ASP.NET fogja okozni. Mivel az Identity Selector kontroll XML-t ad vissza és mi azt akarjuk a szerverre postázni, ezért "A potentially dangerous Request.Form value was detected from the client" hibaüzenetet fogunk kapni. A mezei megoldás persze az, hogy letiltjuk a validálást szerver oldalon, de mivel éppen olyan kódot írunk, amit majd a login oldalon is fogunk használni, ez nem túl bölcs lépés lenne.

Ehelyett sokkal szebb feladat, hogy megírjuk a Server.HtmlEncode függvényt kliens oldalra, amivel szépen hátéemelesítjük az XML, hogy aztán majd szerver oldalon egy csapásra vissza tudjuk alakítani. Sajnos ezt nekünk kell megírnunk, mert az AJAX Library nem tudja (pedig illene neki). Sokféle megoldás közül választhatunk, én úgy döntöttem, hogy a String osztály funkcionalitását terjesztem ki egy újabb függvénnyel (gyanítom, hogy ez nem tökéletes, de eddig működött nálam):

    String.prototype.htmlEncode = function() {
        s = this;
        a = s.split( "&" ); s = a.join( "&amp;" );
        a = s.split( "<" ); s = a.join( "&lt;" );
        a = s.split( ">" ); s = a.join( "&gt;" );
        a = s.split( '"' ); s = a.join( "&quot;" );
        return s;    
    }

Ezt felhasználva módosíthatjuk a selectInfoCard() metódust:

    hIT.value = new String( oIS.value ).htmlEncode();

Ha eddig ügyesek voltunk, akkor szerver oldalon egyetlen sorral el tudjuk érni a kártyát reprezentáló XML tokent:

    string xmlToken = this.Server.HtmlDecode( this.hidIdentityToken.Value );

Ezzel tehát megoldottunk a bejelentkezéshez szükséges adatok elkérését és elküldését. De ez persze nem fog mindig működni… folyt. köv. 

 

Technorati tags: , ,

CardSpace és ASP.NET AJAX 2: A terep előkészítése

(Aki esetleg lemaradt volna a bevezetőről, ezen a címen találja meg.)

A webszerver előkészítése

A legelső feladatunk, hogy létre kell hoznunk egy SSL-es webhelyet. Ehhez IIS-re lesz szükségünk, tehát aki eddig hozzám hasonlóan a Visual Studio beépített fejlesztői webszerverét használta, most egy kicsit búcsúzzon el tőle és kapja elő az Add/Remove Programsot és lökje fel az IIS-t a gépére.

Ha az IIS telepítést megelőzte az ASP.NET telepítése, akkor következő lépésként érdemes beregisztrálni az ASP.NET-et az IIS-be, amire az aspnet_regiis /i az ideális eszköz (megtalálható a %FrameworkDir%v2.0.50727 mappában).

Az SSL-hez szükséges tanúsítvány létrehozását az IIS 6 Resource Kitben található SelfSSL eszközre bízzuk. Az egész csomag ingyenesen letölthető, aki nem akar sokmindent feltelepíteni, válassza ki csak ezt a telepítőből. A mindössze 48 KB-os parancssori alkalmazás elérhető a Start menüből, ami lényegében egy command prompt ablak a %ProgramFiles%IIS ResourcesSelfSSL mappára.

Ha paraméterezés nélkül indítjuk, akkor SSL-esíti a localhostunkat, ami nálunk most nem egészen jó. A végső alkalmazásunkat valószínűleg interneten keresztül szeretnénk publikálni, azaz teljes FQDN-nel http://www.akarmi.hu formában, amit a böngészők az Internet Zónába sorolnak. Ha azt szeretnénk, hogy a fejlesztői környezetünk is ebben legyen, akkor pontnak kell lennie a hostname-ben és természetesen a tanúsítványban. Tehát használjuk a /N paramétert, valahogy így (a /T rögtön a trusted root CA-k közé is beteszi a tanúsítványt, ami most fontos nekünk):

    selfssl.exe /N:CN=bambus.local /T

A programot mindenképpen rendszergazdai jogokkal indítsuk, sokféle hibaüzenettől kímélhetjük meg magunkat! Ha sikerült "Failed to build the subject name blob: 0x80092023" hibaüzenetet kapni, akkor kifelejtetted a CN=-t!

Névfeloldás konfigurálása

A bambus.local-t természetesen nem fogja megtalálni a böngésző, hacsak nem segítünk neki azzal, hogy felvesszük a hosts fájlba ezt a rekordot. Irány a Notepad és a %WinDir%System32Driversetchosts fájlba vegyük fel a következő rekordot:

    127.0.0.1	bambus.local

Ha ez így megvan, akkor teszteljük az SSL működését például azzal, hogy böngészőben megnyitjuk a https://bambus.local/print.gif útvonalat. Adig etessük a böngészővel a tanúsítványokat, amíg már nem jelez hibát!

Ezek után úgy tűnik érdemes újraindítani az egész gépet a Windows CardSpace szolgáltatást (net stop idsvc), különben később egy barátságos, ám semmitmondó "Windows CardSpace encountered an error when verifying the identity of the site and cannot continue. To close Windows CardSpace, click OK. To temporarily prevent Windows CardSpace from starting, click Disable." üzenet fog megjelenni a kártya kiválasztásakor.

Tanúsítványok előkészítése

Az előző lépésben használt SelfSSL létrehozott egy gyönyörű tanúsítványt, és azt be is konfigurálta az IIS-ben. Erre a tanúsítványra sajnos még nekünk is szükségünk lesz, amikor a kliens által elküldött kártya információkat próbáljuk kibogarászni szerver oldalon. Fájdalom, de alapértelmezés szerint a webalkalmazást futtató ASPNET (XP-n) vagy Network Service (Windows Server 2003-on) felhasználónak nincs joga hozzáférni ehhez a tanúsítványhoz.

A bátrabbak elindíthatják a Certificates MMC snap-int, megnyitva a Local Computer tanúsítványtárát látni fogjuk a szóban forgó tanúsítványt, csak éppen a hozzáférést nem tudjuk szabályozni rajta.

A tanúsítványokat a fájl rendszer ACL-jei védik, mégpedig a %AllUsersProfile%Application DataMicrosoftCryptoRSAMachineKeys mappában. Ha vadulni szeretnénk, akkor persze állítgathatjuk itt az ACL-eket, de ha sebészi beavatkozást szeretnénk végezni, akkor célszerszámra lesz szükségünk. A WseCertificate3.exe a WSE 3.0 része, és telepítés után a C:Program FilesMicrosoft WSEv3.0Tools mappában találjuk meg mind a 40 KB-ját.

Természetesen ezt az eszközt is rendszergazdai jogokkal indítsuk, különben "Keyset does not exist" hibaüzenetet kapunk. Ugyanezt a hibaüzenetet kapnánk később is, ha a szerver oldali kódunknak nincs joga elérni egy tanúsítványt.

Válasszuk a Local Computer locationt és a Personal store-t, majd kattintsunk az alsó View Private Key File Properties gombra, ahol a szokásos ablakban adjunk legalább Read & Execute jogot az ASPNET vagy a Network Service felhasználói fióknak.

Mind a SelfSSL.exe, mind pedig a WseCertificate3.exe fájlt érdemes eltenni magunknak, hiszen ezekre még akkor is szükségünk lesz, amikor az alkalmazásunkat a végleges szerverre telepítjük!

Folyt. köv.

 

Update: nem kell az egész gépet újraindítani, elég a Windows CardSpace szolgáltatást.

Technorati tags: , ,

CardSpace és ASP.NET AJAX 1: Bemelegítés

Aki körbekattintgatott már az új Vistáján vagy alaposan megnézi, mi kúszik a gépére a Windows Update-ről, annak biztosan feltűnt egy új ikon a Vezérlőpulton. A Windows CardSpace, vagy gyerekkori nevén InfoCard, bizony még a Workflow Foundationnél is mostohább része a .NET Framework 3.0-nak, róla ugyanis még összesen nem beszélt annyit senki, mint a három Foundation bármelyik apró részletéről.

Az egésznek az a célja, hogy egyszerűsítse a felhasználók számára az egyes webhelyeken használt identitások kezelését és persze lehetőleg biztonságosabbá is tegye azt, miközben nem megy bele platformfüggő és csúnya kapitalista, monopolista, erőfölénnyel visszaélő zsákutcákba, ami miatt aztán egy remek alapötlet kudarcba fulladna (ld. Passport). Fontos változás az eddigiekhez képest, hogy a rendszer nem azon alapul, hogy valamilyen központi szerveren tárolódnak az indentitásunkhoz kapcsolódó adataink, hanem a saját gépünkön és mi választhatjuk ki, hogy éppen akarjuk-e valamelyiket használni vagy sem és közben egyértelműen látjuk, hogy milyen adatokat osztunk meg az adott webhellyel.

Akinek sikerült felkelteni az érdeklődését, bőven olvashat még róla a közösségi oldalán, az MSDN-en, vagy akár a Wikipedián. Ennek a cikknek (és a folytatásainak) az a célja, hogy segítsen beépíteni a CardSpace alapú hitelesítést webalkalmazásainkba, ugyanis azt vettem észre, hogy a weben található példák sok sebből véreznek.

A teendők szépen sorban:

  1. Bemelegítés: ezt olvasod most, csak hogy lássuk, mibe is vágjuk a fejszénket.
  2. A környezet előkészítése: IIS, tanúsítványok, SSL, ACL-ek.
  3. Adatbekérés és elküldés megvalósítása weboldalon: itt lesz egy kis Microsoft AJAX, hogy kevesebb JavaScriptet kelljen írnunk, mert a ValidateRequesttel és a HTML encodinggal lesz egy adag szívás.
  4. AJAX ide vagy oda, külön részben küzdünk a böngészőfüggőséggel, ha esetleg valaki hiányolná.
  5. Adatok feldolgozása szerver oldalon: titkosított XML értelmezése szinte élményszámba megy, előre lehet örülni neki.
  6. Bejelentkezés megvalósítása: ez már szinte sétagalopp, még ha a Membership providert meg is kell kerülni.

A dolog lényege igen röviden:

Kell készítenünk egy olyan SSL-es weblapot, amelyen JavaScripttel felizgatunk egy OBJECT tag-et, hogy az jelenítse meg a kártya kiválasztó ablakot (ami nagyon hasonló a Vezérlőpultos applethez). Ez a kontroll XML-t fog nekünk visszaadni, amit "HTML enkódolva" el kell küldenünk a szerverre, hogy ott aztán a megfelelő XML tageket tanúsítványok segítségével értelmezzük. Ennek a nagy küzdelemnek az eredménye az lesz, hogy átmegy egy ID a kliensről a szerverre, ami egyedi a kártya-webhely párosra nézve, tehát mi szerver oldalon elhihetjük, hogy ez az ID a világon csak egyetlen felhasználónak áll a rendelkezésére. Ezt eltesszük egy adatbázisba, hogy amikor bejelentkezéskor felhasználó név és jelszó helyett ezt kapjuk meg, akkor megnézzük, hogy ott van-e és ha igen, akkor tudjuk, hogy melyik felhasználóhoz tartozik, ugyanis az kell ahhoz, hogy a Membership providerrel elhitessük, hogy a felhasználó már azonosította magát.

Vonzó, nem? smile_tongue

Folyt. köv.

 

Technorati tags: , ,

Saját action XML-Scriptben

Előző bejegyzésemben már írtam arról, mennyire jól használhatóak az akciók XML-Scriptben, hiszen deklaratívan írnak le kliens oldali funkcionalitást, amiből egyszer majd JavaScript keletkezik és ontja a magicet a böngészőben. Sajnos a beépített eksönök száma nem túl nagy, de szerencsére a rendszer nyitott, készíthetünk sajátot, hát nosza! Egy jól működő saját akció feltételei:

  • Saját osztályt kell készíteni JavaScriptben, ami a Sys.Preview.Action osztályból származik.
  • Implementálni kell a performAction metódust. Ha valaki megnézi a PreviewScript.debug.js fájlban, hogyan implementálták az ősosztály metódusát, nagyon elegáns megoldással találkozik, mindössze ennyi van benne: Error.notImplemented(); smile_teeth
  • Definiálni kell egy descriptort, hogy az action deklaratívan is használható legyen.

Essünk neki! Hozzunk létre egy új ASP.NET AJAX CTP-Enabled Web Site-ot Visual Studioban, majd adjunk a projekthez egy JScript File típusú elemet, a neve legyen StatusAction.js. Ebben a fájlban egy olyan akciót fogunk elkészíteni, amely az ablak státuszsorán tud megjeleníteni egy üzenetet.

Ebben a JavaScript fájlban mindenek előtt hozzunk létre egy új névteret, például Budapest néven:

    Type.registerNamespace( 'Budapest' );

Hozzunk létre egy új osztályt ebben a névtérben StatusAction néven. Az osztálynak legyen egy text tulajdonsága, melyet egy get_text és egy set_text metóduson keresztül lehet manipulálni, valamint írjuk meg a szükséges performAction metódust, amiben megjelenítjuk az ablak státuszsorában a text tulajdonság értékét. Az osztály teljes kódja valahogy így fest:

    Budapest.StatusAction = function() {
        this._text;
    }

    Budapest.StatusAction.prototype = {
        get_text : function() {
            return this._text;
        },
        
        set_text : function( value ) {
            this._text = value;
        },
        
        performAction : function() {
            window.status = this._text;
        }
    }

Hozzuk létre a deklaratív használathoz szükséges descriptort, amely az egyedüli text tulajdonságot tartalmazza:

    Budapest.StatusAction.descriptor = {
        properties: [ { name: 'text', type: String } ]
    }

Végül ne felejtsük el beregisztrálni az osztályt a Sys.Preview.Action osztály gyermekeként:

    Budapest.StatusAction.registerClass( 'Budapest.StatusAction', Sys.Preview.Action );

Ezzel az action készen is van, használjuk fel! Hozzunk létre egy ASPX oldalt, adjuk hozzá a ScriptManager kontrollt, majd módosítsuk úgy, hogy a PreviewScript.js és az előbb létrehozott StatusAction.js kezeléséről is gondoskodjon:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />
            <asp:ScriptReference Path="StatusAction.js" />
        </Scripts>
    </asp:ScriptManager>

Tegyünk az oldalra egy egyszerű gombot, amire kattintva majd megjelenítünk egy szöveget a státuszsoron:

    <input type="button" id="btnWriteStatus" value="Írás a státuszsorra" />

Az XML-Scriptünk lényegében a szokásos, mindössze egyetlen apróságra kell figyelnünk: a létrehozott Budapest névterünkhöz létre kell hoznunk egy XML névteret is. Korábban ezt azért nem használtuk, mert az XML-Script parser végignézi az alábbi névtereket az osztályok után kutatva:

  • Sys
  • Sys.UI
  • Sys.Net
  • Sys.Preview
  • Sys.Preview.UI
  • Sys.Preview.Net
  • Sys.Preview.Data
  • Sys.Preview.UI.Data
  • Sys.Preview.Services.Components

A teljes XML tehát így fest:

  <script type="text/xml-script">
        <page xmlns="http://schemas.microsoft.com/xml-script/2005" xmlns:b="Budapest">
            <components>
                <button id="btnWriteStatus">
                    <click>
                        <b:StatusAction text="Üzenet XML-Scriptből!" />
                    </click>
                </button>
            </components>
        </page>
  </script>        

Mivel az action valójában egy osztály, ezért természetesen használhatjuk kódból is, nem csak deklaratívan. Például, ha létrehozunk a HEAD-ben egy SCRIPT elemet és azon belül egy pageLoad metódust, az automatikusan le fog futni, amint az oldal betöltődött. Itt már van lehetőségünk a saját osztályunk használatára:

    <script language="javascript" type="text/javascript">
        function pageLoad()
        {
            var action = new Budapest.StatusAction();
            action.set_text( 'Üzenet kódból' );
            action.performAction();
        }
    </script>

Látható, hogy saját actiont készíteni nem ördöngősség, bár kétségkívül JavaScriptet kell hozzá írni. Ha viszont ezen túl vagyunk, már használhatjuk a kényelmes deklaratív megközelítést, amihez egyszer majd remélhetőleg valamilyen designer eszközt is kapunk….

A fenti StatusAction teljes forráskódja letölthető a devPORTALról.

 

Technorati tags: ,

Action XML-Scriptben

Korábban már írtam az ASP.NET AJAX-ban található actionökről, mégpedig az újraküldés figyelése kapcsán. Az ott bemutatott megoldás az ASP.NET AJAX Control Toolkiten alapult, így szerver oldali kontrollokat és logikát tételezett fel.

Jó hír, hogy van lehetőség actionök használatára a kliens oldalon, ehhez azonban az ASP.NET AJAX Futures CTP-re lesz szükségünk. Telepítsük fel, majd hozzunk létre Visual Studioban egy ASP.NET AJAX CTP-Enabled Web Site-ot. Váltsunk át markup nézetbe a Default.aspx-en, majd adjunk az oldalhoz egy kliens oldali gombot és egy SPAN elemet, valahogy így:

    <span id="lblResult">Egy egy SPAN, itt jelenik majd meg az eredmény.</span>
    <input type="button" id="btnChange" value="Változtatás" />

A továbbiakhoz szükség lesz a PreviewScript.js fájlra, ezért módosítsuk a ScriptManagert:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />
        </Scripts>
    </asp:ScriptManager>

A formon belül hozzunk létre egy XML-Script blokkot, azon belül pedig egy-egy referenciát a fenti két elemre. A SPAN elérhető a Sys.Preview.UI.Label, az INPUT type="button" pedig a Sys.Preview.UI.Button osztályon keresztül, például így:

  <script type="text/xml-script">
        <page xmlns="http://schemas.microsoft.com/xml-script/2005">
            <components>
                <label id="lblResult" />
                <button id="btnChange">
                </button>
            </components>
        </page>
  </script>        

A button elemet szándékosan üresen hagytam, ide fogjuk ugyanis hozzáadni az eseménykezelőket. A gomb click eseményére szeretnénk reagálni, mégpedig úgy, hogy megváltoztatjuk a cimke szövegét, ami a Label osztály text tulajdonságának köszönhetően gyerekjáték. Ehhez használhatjuk a setPropertyAction akciót:

    <button id="btnChange">
        <click>
            <setPropertyAction target="lblResult" property="text" value="Új szöveg a SPAN-be" />
        </click>
    </button>

Ki lehet próbálni, ennek így működnie kell! Újonnan megszerzett tudásunkkal próbáljuk meg átírni a gomb szövegét is! A fenti módon ez sajnos nem fog sikerülni, mert a Button osztálynak nincs text tulajdonsága, vége a gyereknapnak. Ha valaki tudja, hogy miért nincs, írja meg, én hiányolom!

A jó hír, hogy a setPropertyActionnek – és néhány más akcióknak is – van egy propertyKey tulajdonsága is, amivel nem csak a target osztály property tulajdonságát lehet beállítani value értékre target.property=value formában, hanem a target.property.propertyKey értékét is. Ennyi segítséggel felvértezve már ki is lehet találni a megoldást:

    <setPropertyAction target="btnChange" property="element" 
propertyKey="value" value="Új szöveg a gombra" />

Ez persze így már nem volt nehéz, hiszen a HTML kódból nyilvánvaló, hogy a gomb szövegét az INPUT elemen belül a value tulajdonság határozza meg. De mi van akkor, ha az oldalon egy DIV tartalmát szeretnénk átírni. A kérdés tehát, hogy az alábbi elem megjelenő szövege hogyan módosítható a setPropertyAction segítségével:

    <div id="pnlResult">Ez itt egy DIV, aminek nincs kliens oldali kontroll megfelelője.</div>

A DIV elemhez nem tartozik közvetlenül típusos kliens oldali kontroll a Sys.Preview.UI névtérben, tehát látszólag meg vagyunk lőve. Sebaj, használjuk minden kontrollok ősét, a Sys.UI.Control osztályt és azzal definiáljunk egy referenciát a label és a button tag környékére:

    <control id="pnlResult" />

Érdekesség, hogy a Control osztály nem a Sys.Preview.UI névtérben van. Mivel minden kontroll osztály innen örökli a funkcionalitásának egy részét, érdemes megnézni, hogy az ős mi mindent tud a C:Program FilesMicrosoft ASP.NETASP.NET 2.0 AJAX Extensionsv1.0.61025MicrosoftAjaxLibrarySystem.Web.Extensions1.0.61025.0MicrosoftAjax.debug.js fájlban.

Folytatva a fenti példát hátra van még az, hogy a gombra kattintáskor át kellene írnunk a DIV szövegét. A Control osztálynak sincs text tulajdonsága, de legalább el lehet kérni a burkolt HTML elemet az element tulajdonságon keresztül, és annak már meg lehet változtatni a tartalmát a jól ismert innerHTML tulajdonság átírásával:

    <setPropertyAction target="pnlResult" property="element" 
propertyKey="innerHTML" value="Új szöveg a DIV-re" />

A setPropertyAction csak egy a sokféle akció közül, érdemes megismerkedni a többivel is!

A fenti példa teljes forráskódja letölthető a devPORTALról.

 

Technorati tags: ,

Kattints, de csak egyszer! Action!

Aki készített már adatkezelő weblapot átlagfelhasználók számára, biztosan találkozott már azzal a feladattal, hogy biztosítani kellett, hogy a felhasználó csak egyszer küldhesse el a kitöltött űrlapot feldolgozásra. Az újraküldés figyelésére általánosan fogalmazva két lehetőség van:

  • Újraküldés figyelése szerver oldalon. Ez nagyon kényelmes, szerver oldali programozást biztosít, de nem egyszerű feladat és könnyen előfordulhat, hogy űrlap típusonként más megoldást kell alkalmaznunk. Az újraküldést ellenőrző logika ráadásul látványosan elcsúnyítja az adatok feldolgozását végző kódot.
  • Újraküldés tiltása kliens oldalon. Ez a megoldás lényegesen jobban kezelhető felhasználói felületet eredményez, nehézsége, hogy JavaScriptet kell írni hozzá és persze nem bolondálló a megoldás (nem árt, ha ettől függetlenül ellenőrzünk még egyszer szerver oldalon is).

A jobb felhasználói élmény, a kód újrafelhasználhatósága és a szerver tehermentesítése érdekében sokan választják a második megoldást. A feladat többféleképpen megoldható, nézzünk most egy újkeletű megoldást, használjuk az ASP.NET AJAX Control Toolkitet!

Az egyszerű példa kedvéért korlátuzzuk a feladatot arra, hogy egy gombot kell letiltanunk abban az esetben, ha a felhasználó rákattint. Hozzunk létre egy új AJAX Control Toolkit Web Site típusú projektet Visual Studioban, majd dobjunk a generált Default.aspx oldalra egy TextBox (ID=txtName), egy Label (ID=lblResult) és egy Button (ID=btnGo) kontrollt.

    <asp:TextBox ID="txtName" runat="server">Gipsz Jakab</asp:TextBox>
    <asp:Button ID="btnGo" runat="server" Text="Elküldés" OnClick="btnGo_Click" />
    <asp:Label ID="lblResult" runat="server" />

Írjunk a gombhoz egy rövid eseménykezelőt, amelyben kissé belassítjuk az oldal végrehajtását:

    protected void btnGo_Click( object sender, EventArgs e )
    {
        System.Threading.Thread.Sleep( 5000 );
        this.lblResult.Text += String.Format( "Köszönjük, {0}!", this.txtName.Text );
    }

Az újraküldés elkerülésére használjuk a Control Toolkitben lévő AnimationExtender kontrollt! Dobjunk belőle egy példányt az oldalra és állítsuk be úgy, hogy a gombunk funkcionalitását terjessze ki (TargetControlID="btnGo").

Sajnos a kód többi részét kézzel kell megírnunk, ezért váltsunk át markup forráskód nézetre. Az AnimationExtender tag-en belül hozzünk létre egy Animations elemet, ez fogja tartalmazni az animációkat, majd azon belül egy OnClick elemet – itt lesznek a kattintáskor lefutó animációk:

    <ajaxToolkit:AnimationExtender ID="ae" runat="server" TargetControlID="btnGo">
        <Animations>
            <OnClick>
            </OnClick>
        </Animations>
    </ajaxToolkit:AnimationExtender>

A lehetséges effektusoknak két fajtája létezik a Control Toolkitben:

  • Az animációk valaminek az értékét változtatják egy kezdőállapottól egy végállapotig a megadott időtartam alatt. Ilyen például a mozgatás (Move), az átméretezés (Resize) vagy a halványítás (FadeIn, FadeOut), melyek az Animation ősosztályból származnak.
  • Lényegében az animációk speciális esetének tekinthetők az akciók, melyek az Animationből származó Action osztály gyermekei. Az akciók olyan animációk, amelyek valójában nem is animálnak, hanem egyszerűen csak megtörténnek egy pillanat alatt. Ilyen például egy kontroll elrejtése vagy megjelenítése (Hide), átlátszóságának állítása (Opacity), valamilyen kód futtatása (Script) vagy éppen egy kontroll engedélyezése vagy tiltása (Enable).

A mi célunknak remekül megfelel az utolsóként említett EnableAction, amivel lehet engedélyezni vagy tiltani egy kontrollt. Adjuk hozzá az OnClick elemhez ezt az "animációt":

    <EnableAction Enabled="false" />

A StyleAction segítségével megváltoztathatjuk az adott kontroll stílusát, például elküldés esetén a homokóra egérkurzort rendelhetjük hozzá. Ehhez az alábbi sort kell csak beírnunk az EnableAction elem helyett:

    <StyleAction Attribute="cursor" Value="wait"/>

Hogy még feltűnőbb legyen a felhasználó számára, hogy történik valamit, kattintás esetén még a gomb szövegét is átírhatjuk. Erre elméletben remek lenne a PropertyAnimation, nekem viszont nem sikerült működésre bírnom a januári CTP változattal. Sebaj, legalább megnézhetjük, hogyan használható a ScriptAction! Ebben az esetben nekünk kell megírnunk azt a minimális kódot, ami a feladatot elvégzi, szerencsére használhatjuk a Microsoft AJAX Library metódusait, például a $get segítségével elkérhetünk egy a gombra mutató referenciát:

    <ScriptAction Script="$get('btnGo').value='Türelem...';"/>

Szerencsére alkalmazhatjuk egyszerre mindhárom animációt is, ekkor azonban be kell ágyaznunk őket egy Sequence vagy Parallel elembe, valahogy így:

    <ajaxToolkit:AnimationExtender ID="ae" runat="server" TargetControlID="btnGo">
        <Animations>
            <OnClick>
                <Sequence>
                    <EnableAction Enabled="false" />
                    <StyleAction Attribute="cursor" Value="wait"/>
                    <ScriptAction Script="$get('btnGo').value='Türelem...';"/>
                </Sequence>
            </OnClick>
        </Animations>
    </ajaxToolkit:AnimationExtender>

Hát nem nagyszerű?! Megint nem írtunk egyetlen sornyi JavaScript kódot sem és van egy remélhetőleg cross-browser megoldásunk – annyira az, amennyire az AnimationExtender, tehát éppen eléggé smile_wink!

Mindez nagyon szép, de mi van akkor, ha egy UpdatePanelt szeretnénk frissíteni? Gond egy szál se, akkor használjuk az UpdatePanelAnimationExtender kontrollt, amely szintén az AJAX Control Toolkit része. Ez annyiban más, hogy OnClick helyett OnUpdating és OnUpdated eseményei és elemei vannak, melyek a frissítés előtt és után futnak le. Itt már gondoskodnunk kell arról, hogy az elküldés előtt módosított értékeket a válasz megérkezése után visszaállítsuk, azaz újra engedélyezzük a kontrollt. Ez az EnableAction és a StyleAction esetén nem annyira bonyolult:

    <ajaxToolkit:UpdatePanelAnimationExtender ID="ae" runat="server" TargetControlID="up">
        <Animations>
            <OnUpdating>
                <Sequence>
                    <EnableAction Enabled="false" />
                    <StyleAction Attribute="cursor" Value="wait"/>
                </Sequence>
            </OnUpdating>
            <OnUpdated>
                <Sequence>
                    <EnableAction Enabled="true" />
                    <StyleAction Attribute="cursor" Value="hand"/>
                </Sequence>
            </OnUpdated>
        </Animations>
    </ajaxToolkit:UpdatePanelAnimationExtender>

A gond a gomb kezelésénél van. Először is az UpdatePanelAnimationExtender az UpdatePanel vezérlőt terjeszti ki (lásd fent TargetControlID="up"), nem a gombot. Tehát ha a gomb szövegét akarjuk megváltoztatni, akkor vagy az AnimationTarget tulajdonságot kell használnunk, vagy ésszel kell megírnunk a ScriptActiont. Az utóbbi esetben az jelenti a nehézséget, hogy elküldés előtt el kell tárolnunk a gomb eredeti szövegét, amit majd vissza kell állítanunk a válasz megérkezése után. Ehhez használjuk az extender BehaviorID tulajdonságát, amiről a dokumentáció mindössze ennyit ír:

BehaviorID: In cases where you would like to access the client-side behavior for your extender from script code in the client, you can set this BehaviorID to simplify the process.

Ezt felhasználva a teljes kontroll felparaméterezve így fest:

    <ajaxToolkit:UpdatePanelAnimationExtender ID="ae" BehaviorID="animation" 
runat="server" TargetControlID="up"> <Animations> <OnUpdating> <Sequence> <EnableAction Enabled="false" /> <StyleAction Attribute="cursor" Value="wait"/> <ScriptAction Script="var a = $find('animation');
var b = $get('btnGo');
a._originalText=b.value;
b.value='Türelem...';" />
</Sequence> </OnUpdating> <OnUpdated> <Sequence> <EnableAction Enabled="true" /> <StyleAction Attribute="cursor" Value="hand"/> <ScriptAction Script="$get('btnGo').value=$find('animation')._originalText;"/> </Sequence> </OnUpdated> </Animations> </ajaxToolkit:UpdatePanelAnimationExtender>

Nos, az eredmény talán bonyolultabb, mint azt előtte sejtettük, sőt, talán nem ez a létező legegyszerűbb megoldás, de az biztos, hgoy nem kellett egyetlen sort sem kódolnunk a kliens oldalra és a kód egészen áttekinthető! Az arany középút valószínűleg az EnableAction és az UpdateProgress kontroll használata.

A teljes forráskód letölthető a devPORTAL információtárából, jó kísérletezést!

Technorati tags: ,

ASP.NET AJAX: “Honnan fogom én ezt megtanulni?”

Igen gyakran keresnek meg olyan kérdéssel, hogy ajánljak egy könyvet, ahonnan ezt vagy azt (az éppen szóban forgó) technológiát meg lehet tanulni. Fontos, hogy lehetőleg egynél többet ne említsek, mert senkinek nincs ideje sokszáz oldalas szakkönyvekből többet is elolvasni. Így volt ez a mai MSDN Kompetenciák Egyetemének szünetében SQL Server kapcsán, és így volt a tegnapi előadásom után az ASP.NET AJAX-szal kapcsolatban is.

A helyzet az, hogy még soha nem fordult elő, hogy röviden tudtam volna azt válaszolni, hogy itt ez a remekmű, tessék, benne van minden. Annak idején, amikor a .NET 1.1-ről nekiláttunk megírni az azóta gyakran csak "Albert István féle fekete .NET biblia"-ként emlegetett könyvet, még volt ilyen ambíciónk, személy szerint bennem ez azóta teljesen szertefoszlott: a technológiák önmagukban is összetettek, sokrétűek, ráadásul összefüggenek egymással. Ma már szinte nincs alkalmazás adatbázis nélkül, tehát nem elég ismerni teszem azt a C#-ot, bizony konyítani kell az SQL-hez is. Ha webalkalmazásról van szó, akkor pedig nem elég az ASP.NET, tudni kell, hogy alul a csupasz HTTP van a maga kérés-válasz modelljével, a kliens oldalon pedig nem tud más futni, mint JavaScript. Ráadásul a kívánt eredmény eléréséhez ezek általában önmagukban nem elegek, együtt kell alkalmazni őket.

Az ASP.NET AJAX kapcsán például a következő három könyv került a kezembe:

Mindhárom könyvről elmondhatom, hogy nagyon jó kiindulópont lehet az AJAX megismeréséhez, ugyanis onnan indulnak, hogy mi kell egy aszinkron weboldalhoz, mi az az XmlHttpRequest, hogyan használható az XML böngészőben és persze ismertetik a JavaScriptet is, majd rátérnek az Atlasra. Kétségkívül van átfedés a három könyv között, és persze van különbség a téma feldolgozásában is. Mégis mindháromról elmondható, hogy nem adnak életképes tudást, ugyanis a termék béta verziójához készültek. Azóta átneveztek szinte mindent, és megszületett az ASP.NET AJAX Futures CTP is. Hogy csak egy példát mondjak, a Microsoft AJAX Library-ről szóló fejezetekből inkább ihletet tudtam meríteni, mintsem a példákat használni.

A hasonló helyzetek elkerülése érdekében a leggyakoribb, visszatérő problémák:

1. A Futures CTP megjelenése miatt született egy Sys.Preview névtér, és a legtöbb kliens oldali kontroll egyelőre ebben található meg. Honnan lehet megtudni, hogy melyik hol van? Ne féljünk belenézni a PreviewScript fájl debug változatába, onnan sok minden kiderül. (A teljes útvonal, kéretik fejből tudni: C:Program FilesMicrosoft ASP.NETASP.NET 2.0 AJAX Futures January CTPv1.0.61025ScriptLibraryMicrosoft.Web.Preview1.0.61025.0Microsoft.Web.Resources.ScriptLibrary.PreviewScript.debug.js) Például, ha a Button osztályt keressük, a fájl megnyitása után keressünk rá a ".Button" kifejezésre, és azonnal kiderül, hogy a Sys.Preview.UI névtérben található. Ezzel szemben az ős Control osztály a Sys.UI névtérben van – egyelőre.

2. Sok tulajdonság megszűnt (legalábbis átmenetileg). Több cikk ír például a checkbox enabled tulajdonságáról. Nyissuk meg a fenti fájlt, keressünk rá arra, hogy ".CheckBox", majd görgessünk le addig a sorig, amely úgy kezdődik, hogy "Sys.Preview.UI.CheckBox.prototype", itt található ugyanis az osztály legtömörebb leírása. Enablednek semmi nyoma. Nosza nézzük meg az ősosztályt! Ha lejjebb tekerünk 25 sorral, ezt találjuk:

    Sys.Preview.UI.CheckBox.registerClass('Sys.Preview.UI.CheckBox', Sys.UI.Control);

Ebből látszik, hogy a Control az ős, de annak is csak visible tulajdonsága van, noha az enabled szerintem sokkal hasznosabb lenne. Reméljük egyszer azért visszakapjuk. Másik gyakori eset, hogy a Select osztályt ma például Selectornak hívják, aki nem hiszi, járjon utána!

3. Máshogy kell hivatkozni a szkriptekre a ScriptManagerben. Már nem kell kiírni a teljes névteret, elég a script nevét megadni, valahogy így:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />
        </Scripts>
    </asp:ScriptManager>

Ha nincs $get függvényünk sem, vagy az alábbi függvényhívás nem dob fel egy alert ablakot automatikusan az oldal betöltése után, gyanakodhatunk erre a hibára:

    function pageLoad()
    {
        Sys.Preview.UI.Window.messageBox( "Az oldal inicializálása befejeződött." );
    }

4. Szerver oldalon is megjelent egy preview névtér, a Microsoft.Web.Preview, ami a hasonló nevű DLL-ben található. De a korábbi Microsoft.Web ma már System.Web, hogy jobban illeszkedjen az ASP.NET-hez, így például az UpdatePanel a System.Web.UI névtérben van, mint a Control és a Page osztály, csak éppen a System.Web.Extensions.dll-ben, ami azonban nem a C:WINDOWSMicrosoft.NETFrameworkv2.0.50727, hanem a C:Program FilesMicrosoft ASP.NETASP.NET 2.0 AJAX Extensionsv1.0.61025 mappában lelhető meg (és persze a GAC-ban).

Mindez csak ízelítő, hiszen sok helyen nem csak, hogy a szintakszis más, de egy adott probléma megoldásához elindulni is máshonnan kell, mint ahogyan az a könyvben szerepel. Sajnos ez az egyetlen lehetőség van arra, hogy a termék megjelenéséhez időben minél közelebb a polcokra kerülhessen egy könyv. Nem lehet hónapokat várni, hiszen addigra jön egy újabb verzió, vagy ha nem, a konkurencia biztosan.

Röviden: ebben a szakmában a hagyományos szakirodalom napjai meg vannak számlálva. Van, aki ezt már akkor megérezte, amikor nyilvánvalóvá vált, hogy a Pascalhoz még elég volt egy könyv, de ma már MSDN nélkül nem lehet fejleszteni.

De akkor hogyan, honnan? Úgy tűnik, hogy a legdinamikusabb és legbőségesebb (és persze legkevésbé ellenőrzött) forrás kétségkívül az internet: cikkek, blogok, folyóiratok. Na de kinek van ideje mindezt végigolvasni? Vegyük a mostani konferencia esetét: Dávid Zoltán, Gincsai Gábor, Kereskényi Róbert és Virág András kollégáimmal együtt hosszú heteket áldoztunk arra, hogy az a tartalom, amit a résztvevők láttak (láttatok) így összeálljon. A fent említett könyveken kívül – kis túlzással – szinte mindent elolvastunk, ami az adott témában az interneten rendelkezésre áll, azokat kipróbáltuk, hogy bennünk olyan szinten álljanak össze az ismeretek, hogy azt már megemésztve, rendszerezve tovább tudjuk adni. Nem csak teszt alkalmazásokat készítettünk, hanem bizony éles projektekben is bevetettük az AJAX-ot. A bökkenő csak az, hogy mindez összesítve komoly emberhónapokat jelent, amit nem mindenki engedhet meg magának.

Visszatérve a kezdeti problémához: hogyan lehet a leghatékonyabban elsajátítani egy technológiát, mikor mindenki benne van a napi robotban és sürget a határidő? Csak úgy, hogy előemésztett forrásokkal dolgozik az ember. Aki az önálló tanulást preferálja, ilyen előemésztett forrásként használhatja például a novemberi MSDN konferencia alapján készült .NET 3.0 anyagot a devPORTALon, vagy az ASP.NET 2.0 Induló Készletet, amely remekül megalapozhatja az ASP.NET AJAXos ismereteinket. Aki pedig inkább a csoportos, interaktív tanulást preferálja, annak nem tudok jobbat mondani, mint hogy keressen magának tanfolyamot az adott témában. Sajnos a hivatalos Microsoft tanfolyamok legalább fél évvel a technológia végleges változatának megjelenése után lesznek csak elérhetőek, de lehet olyan céget vagy oktatóközpontot választani, ahol megvan a kompetencia az egyéni, testreszabott tanfolyamhoz, amely a résztvevőkhöz alkalmazkodó szintről indul és olyan tempóban halad, hogy a rendelkezésre álló időt a lehető legjobban kihasználja.

És hogy mi legyen az ASP.NET AJAX-szal? Már töltődnek fel a konferencia előadásai a devPORTALra, lehet csemegézni smile_wink

 

Technorati tags: ,