Monthly Archives: January 2009

Nagy méretű weblapok fotózása – Webpage Capture

Akinek kellett már dokumentálás vagy UI prototípus egyeztetés céljából weblapról képernyőképet készítenie, az tudja, hogy a feladat nem minden esetben egyszerű. Akkor nincs gond, ha az oldal kifér a képernyőre, de mi van akkor, ha függőlegesen csak három képernyőnyi scrollozás után lehet az oldal aljára érni? Ebben az esetben marad Nikhil Web Development Helpere vagy a print screenek egymás alá ragasztgatása. Mivel az előbbi elkezdett nem működni nálam, kénytelen voltam írni egy célalkalmazást.

Mivel évente kb. egy vastag kliens alkalmazást írok, úgy látszik ez lesz az idei termés, íme a Webpage Capture:

Webpage Capture

És íme a kimenet, egy fotó a Weblaborról 976 x 3565 pixel méretben:

WebpageCapture: Weblabor

Bár az alkalmazás egészen pofásra sikerült (a saját gyermek ugye 🙂 ) és használhatóan is működik, azért van egy apró szépséghibája: mivel nem akartam P/Invoke-t használni, ezért inkább egy nem támogatott megoldást választottam a képernyőképek elkészítésére. A WebBrowser kontrollnak van egy DrawToBitmap metódusa, amely bár kiválóan működik, “supports the .NET Framework infrastructure and is not intended to be used directly from your code”.

A másik lényeges pont, hogy a DrawToBitmap azt rajzolja a bitmapbe, amit a WebBrowser kontroll éppen mutat, ezért a rajzolás előtt át kell méretezni a kontrollt akkorára, amekkora a weboldal. A dokumentum mérete pedig a WebBrowser.Document.Body.ScrollRectangle tulajdonságból derül ki.

Lényegében ennyi kódolás kellett a fenti alkalmazás elkészítéséhez, a többi sallang, ami viszont igazán kényelmesen használhatóvá tette, szinte összekattintgatható: a böngészőben korábban használt URL-ek felkínálása és automatikus kiegészítése, az utoljára használt mappa, URL és checkbox állapot felhasználónkénti elmentése, a mentési útvonal tallózása, a beágyazott erőforrásként tárolt ikonokkal ellátott gombok, progress bar és link a status barra, na és persze mindez egy kattintással admin jogok nélkül a webről települve és önmagát frissítve ClickOnce segítségével. Azt kell hogy mondjam, igazán jó dolgok vannak a Windows Formsban, amiket én fontosabbnak ítélek, mint a 3D-ben forgazható grafikonokat 🙂

Aki szeretné kipróbálni az alkalmazást, ide kattintson:

http://www.msdnkk.hu/Storage/balassy/Enclosures/WebpageCapture

Known issue: bizonyos weblapok (pl. index) esetén többször készül el a kimeneti fájl, mert valamilyen okból az oldal letöltését jelző esemény többször sül el. Mindez persze csak akkor idegesítő, ha a View after capture be van kapcsolva. Megoldás: kapcsoljuk ki 🙂

 

Response.Redirect kliens oldalon

Számtalanszor előfordul, hogy egy oldal feldolgozása során a felhasználót át kell irányítanunk egy másik oldalra. Erre vannak jól bevált megoldások, a Response.Redirect, a Server.Transfer vagy a Server.Execute. Mindegyiknek megvan a maga előnye és hátránya, van azonban egy közös nagy hátrányuk, amit hajlamosak vagyunk elfelejteni.

Gyakran kell például Cancel jellegű gombot készítenünk, ami visszairányítja a felhasználót az előző oldalra, ilyenkor általában ezt a megoldást látom:

    <asp:Button ID="btnGo" runat="server" OnClick="btnGo_Click" Text="Vissza a Kezdőlapra" />

    protected void btnGo_Click( object sender, EventArgs e )
    {
        this.Response.Redirect( "Default.aspx" );
    }

Azaz egy szerver oldali gomb, szerver oldali eseménykezelővel. Érdemes megfigyelni, hogy nincs benne semmi olyan, ami szerver oldali feldolgozást igényelne. Az ilyen jellegű átirányításra lehetne egyszerű HTML <a> taget használni, de amikor ezt felvetem, általában azt a választ kapom, hogy a Cancel mellett van egy OK gomb is, ami viszont igenis igényli a szerver oldali logikát és hogy nézne ki, ha az egyik link, a másik pedig gomb lenne.

A Response.Redirectnek azonban van egy közismerten rossz tulajdonsága: kliens oldali átirányítást végez, azaz visszaküld egy HTTP Error 302-t (Object moved), mire a böngésző egy másik HTTP requestben kéri le a hivatkozott oldalt. Felesleges roundtrip, ami bizonyos esetekben elfogadható, a fentiben azonban semmiképpen.

Nosza, készítsünk egy egyszerű gombot, ami kliens oldalon irányít át! Ennyi az egész, csupasz HTML, semmi szerver oldali kód nincs benne:

    <input type="button" value="Vissza" onclick="window.location='Default.aspx';" />        

Ja, hogy ilyen vezérlő nincs a Toolboxon? “És akkor mi van, ember?!”

Csak egy kis JavaScript kell és még barátságosabb lesz a gombunk:

    <input type="button" value="Vissza" onclick="if( confirm('Biztosan?') ) window.location='Default.aspx';" />        

Sőt, ha csak egy Vissza gombra van szükségünk, akkor talán a history-val is próbálkozhatunk, hátha a böngésző kivágja az oldalt a gyorsítótárból és a szerverünk nem is kap kérést:

    <input type="button" value="Vissza" onclick="history.back()" />    

Ez a megoldás nem csak akkor működik, ha Cancel funkcióra van szükségünk, hanem akkor is, ha master-detail jellegű oldalakat készítünk, ahol az egyik listáz minden elemet, a másik oldal pedig egy kiválasztott elem részleteit mutatja. A részletes oldalt gyakran úgy oldjuk meg, hogy a kiválasztott elem azonosítóját query stringben adjuk át, tehát a lista oldalon úgy kell átirányítanunk, hogy az azonosító bekerüljön az URL-be. Ehhez sem kell szerver oldali kód, adakötéssel ez is megoldható:

    <asp:ListView runat="server" DataSourceID="sdsCustomers">
        <LayoutTemplate>
            <asp:PlaceHolder ID="itemPlaceholder" runat="server" />
        </LayoutTemplate>
        <ItemTemplate>
            <%# Eval( "CompanyName" ) %> - <%# Eval( "ContactName" ) %>
            <input type="button" value="Részletek" 
               onclick="<%# Eval( "CustomerID", "window.location='Customer.aspx?Id={0}';" ) %>" /> 
        </ItemTemplate>
        <ItemSeparatorTemplate>
            <br />
        </ItemSeparatorTemplate>
    </asp:ListView>
    
    <asp:SqlDataSource ID="sdsCustomers" runat="server" 
      ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
      SelectCommand="SELECT [CustomerID], [CompanyName], [ContactName] FROM [Customers] ORDER BY [CompanyName]">
    </asp:SqlDataSource>

Itt sem használok szerver oldali kódot, az adatkötés ugyanis runat=”server” nélkül is tud működni. Az aláhúzott kifejezésben az az érdekes, hogy az idézőjelek párosítása szemmel láthatóan rossz, mégis működik 🙂

Adott tehát a lehetőség: lustán és gyorsan olyan kódot írni, amely feleslegesen terheli a szervert és várakoztatja a felhasználót vagy némi kézimunkával mindenkinek a kedvére tenni.

Te melyik utat szoktad választani?

 

A cikkhez tartozó forráskód letölthető innen: ClientRedirectSampleWebSite.zip

Technorati Tags: ,

Azonos típusú fájlok kiszedése alkönyvtárakból

Gyakran előfordul, hogy egy mappa végtelen mennyiségű alkönyvtárából kellene minden fájlt vagy éppen egy feltételnek megfelelő fájlokat kimásolni. Régen erre a Windows Explorer Search funkcióját használtam, de a Vista óta erre a célra egyszerűen nem áll kézre. Mivel nem vagyok Total Commander függő, ezért jobb megoldást kerestem és hamar sikerült kikötnöm a PowerShellnél.

Mint ahogy azt a Powershellnél már megszokhattuk, egyetlen sor a megoldás:

    Get-ChildItem -filter *.xls -recurse | Copy-Item -destination C:celmappa

Elsőre furcsa volt, hogy bár a Get-ChildItemre működik a dir alias, a /s helyett mégis –recurse kell, és a copynak megfelelő Copy-Itemet is kicsit másként kell paraméterezni, hamar hozzá lehet szokni.

Technorati Tags:

Aszinkron kérés leállítása

Mint bármely HTTP kérésnél, AJAX esetén is előfordulhat, hogy a válasz lassan érkezik meg a szervertől. A felhasználó türelmetlen lesz, de nem tudja, mihez nyúljon. Mivel a böngésző Stop gombját feleslegesen nyomogatja, a webfejlesztő feladata marad, hogy az AJAX-os kérés leállítására lehetőséget adjon.

Szerencsére erre van támogatás az ASP.NET AJAX-ban: a kliens oldali Sys.WebForms.PageRequestManager osztály abortPostBack() metódusát kell meghívnunk. Egy UpdateProgress vezérlőnk biztosan lesz, hiszen a felhasználót tájékoztatjuk (ugye!?), hogy valami történik a háttérben. Tegyünk rá egy kliens oldali gombot, amely egy saját JavaScript függvényt hív:

    <asp:UpdateProgress runat="server" 
        AssociatedUpdatePanelID="UpdatePanel1" DisplayAfter="0">
        <ProgressTemplate>
            Frissítés folyamatban, türelem...
            <input type="button" value="Mégsem" onclick="cancelRequest()" />
        </ProgressTemplate>
    </asp:UpdateProgress>

A függvényt akár az oldal head részében is implementálhatjuk, annyira általános:

    <script language="javascript" type="text/javascript">
        function cancelRequest() 
        {
            var mgr = Sys.WebForms.PageRequestManager.getInstance();
            if( mgr.get_isInAsyncPostBack() ) 
            {
                mgr.abortPostBack();
            }
        }
    </script>

Ennyi az egész, lehet kopipésztelni minden oldalra, legyen boldog vele a felhasználó.

A cikkhez tartozó forráskód letölthető innen.

 

System Center Remote Hacking Edition

A tegnapi System Center konferencia után jutott eszembe, hogy van egy csuda egyszerű módja annak, hogy az ember távoli rendszerfelügyeleti szoftverre tegyen szert, ráadásul teljesen ingyen! Sőt, a szoftverhez még egy távoli rendszergazda is jár, ő is szinte ingyen! Mindössze egy egyszerű weblapot kell készítenünk.

A lényeg az, hogy hágjunk át minden szoftver fejlesztéssel és üzemeltetéssel kapcsolatos biztonsági ajánlást és a szerverünkre készítsünk egy olyan weblapot, amellyel bárki bármit feltölthet a szerverünkre és utána futtatja is azt. (Amint megvagyunk, kezdjük el írni az önéletrajzunkat…)

Épp a múlt héten találtunk egy olyan szervert, ahol egy upload.php állományon keresztül bármilyen fájlt fel lehetett tenni a szerverre. Sőt, ha a feltöltött fájl épp .php kiterjesztésű volt, futott is vígan! Nem is kellett hozzá egy hét és máris 3 komplett rendszerfelügyeleti megoldást töltöttek fel “jóakaróink” a szerverre, lett egy g00nshell, egy c99shell és egy r57shell a gépen.

Ezek mind egyetlen 150-170 kB méretű .php fájlok annyi okossággal, hogy miután sikerült feltölteni a szerverre, tényleg bármit elvégezhetünk távolról. Az alábbi screenshotokat egy a Google segítségével talált, most is működő szerverről készítettem.

Kapunk például egy teljes remote file explorert:

c99shell_1

Navigáció a fájlrendszerben, listázás, feltöltés, letöltés, tulajdonságok megtekintése, jogosultságok kezelése, tartalom átírása, szinte Total Commander:

c99shell_13_fileinfo

Ha keresnénk valamit:

c99shell_2_commands

Nem csak wildcardos és regexes keresés van, hanem beépítetten tudja a tipikusan fontos fájlok keresését is:

c99shell_3_search

Az Encoder fülön kódolhatunk és hashelhetünk kedvünkre:

c99shell_4_encoder

A Binding fül:

c99shell_5_binding

Teljes process lista KILL-lel:

c99shell_6_processes

Van FTP szervered? Most már nekem is:

c99shell_7_ftp

A biztonságról mindent egy helyen, az /etc/passwd közvetlen letöltésével:

c99shell_8_security

Ide nekem az adatbázis szervert:

c99shell_9_sql

Tetszőleges PHP kód futtatása:

c99shell_10_exec

Meguntad a honlapod? Egy kattintás átírni mindet:

c99shell_12_deface

És hogy a funkcionalitás teljes legyen, nem csak self remove funkció van, de még feedbacket is küldhetünk a szerzőnek, angolul vagy oroszul:

c99shell_11_feedback

Ráadásként az újabb verzió még frissíteni is tudja magát! Nem beszélve a mailbomb funkcióról…

Mégegyszer: mindez szabadon letölthető az internetről, egyetlen kattintással feltölthető egy webszerverre és böngészőn keresztül ad mindent. Mindössze egy rosszul megírt feltöltő oldal és egy rosszul üzemeltetett szerver kell hozzá. Meg egy lelkes, gyakran hozzá nem értő, de kísérletező kedvű vállalkozó. Ész nem.

A fejlesztő a szokásos hibát követte el: megbízott a felhasználói inputban, semmilyen ellenőrzést nem végzett a feltöltött fájlon! Pedig a legveszélyesebb scenarioról van szó: anonymous felhasználó tölt fel adatot a szerverre. Minden input az ördögtől való!

Az üzemeltető pedig ott hibázott, hogy minden mappára adott futtatási jogot, ráadásul a futtató accountnak túl sok joga van. Ne légy admin! Minimális jogosultságot mindenkinek!

Egy egyszerű Google keresés 114 000 olyan oldalt hozott ki, amely jó eséllyel fertőzött. És ez csak egy a sokféle hacker szkript közül. Aki azt gondolja, hogy mind Linux, nagyot téved, a szkript tökéletesen fut Windowson is. Aki pedig azt gondolja, hogy csak az van bajban, aki PHP-t futtat a szerveren, az is téved. Van ugyanilyen ASP.NET-ből is, nem is egy…

 

Technorati Tags: ,,

Barátságos HTTPS átirányítás

Gyakori üzemeltetői feladat, hogy egy oldalt csak biztonságos HTTPS csatornán keresztül szeretnénk elérhetővé tenni. Sajnos nem minden üzemeltetőnek tűnik fel, hogy az is a feladat része, hogy az apró “s” betűt be nem gépelő felhasználókat barátságosan átirányítsuk a biztonságos címre: tegye fel a kezét, aki még nem látott 403.4 Forbidden: SSL is required to view this resource hibaüzenetet. Na ugye. Mennyivel szebb lenne, ha az alapértelmezett hibaüzenet helyett eljuttatnánk a felhasználót oda, ahova indult, csak éppen nem HTTP-n, hanem HTTPS-en keresztül.

A feladat megoldására számos módszer létezik, mutatok egy nagyon primitív megoldást, ami biztosan megy minden webkiszolgáló esetén. A módszer lényege, hogy felüldefiniáljuk az alapértelmezett 403.4 hibaüzenetet egy saját HTML oldallal. IIS 6 esetén például így:

Web Site Properties: Custom Errors

403.4 Custom Error Properties

A megadott sslredirect.htm fájl pedig mindössze ennyi:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
        <head>
            <script language="javascript">
                location.href = 'https://' + location.href.substr( 7 );
            </script>
        </head>
        <body>
        </body>
    </html>

Lehet, hogy a megoldás nem a legszebb, de egyszerűen telepíthető, sima HTML, nem kell hozzá sem ASP.NET, sem PHP, de még XML buherálás sem. Ráadásul nem csak a http://intranet, hanem a http://intranet/sites/akarmi típusú címekkel is megbírkózik, tehát SharePointhoz is tökéletes.

Ti hogyan oldjátok ezt meg?

 

Tábla típusú paraméterek használata ASP.NET-ben

Egyre gyakrabban merül fel az igény, hogy egy SQL lekérdezés egyik paramétereként azonos típusú, ám ismeretlen számú értéket kell átadnunk. Például egy terméket CheckBoxList segítségével több csoportba sorolhat a felhasználó vagy épp egy keresésnél választhatunk több kategória közül. Az SQL Server korábbi verzióinál tipikusan úgy oldottuk meg ezt a feladatot, hogy az értékeket egyetlen string változóban adtuk át, melyben XML vagy egyszerű separator karakterekkel elválasztott értékek szerepeltek. Az SQL Server 2008-tól kezdve viszont már közvetlenül adhatunk át tábla típusú paramétert is.

Egy olyan oldalt akartam készíteni, amely a Northwind adatbázisból azokat a Customereket listázza ki, akik a kiválasztott ország valamelyikében vannak:

Partnerek szűrése ország szerint

ASP.NET szinten az oldal nagyon egyszerű. Fent van egy CheckBoxList, amit egy SqlDataSource tölt fel:

    <asp:CheckBoxList ID="cblCountries" runat="server" DataSourceID="sdsCountries" 
        DataTextField="Country" />
        
    <asp:SqlDataSource ID="sdsCountries" runat="server" 
        ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
        SelectCommand="SELECT DISTINCT TOP 5 Country FROM Customers ORDER BY Country">
    </asp:SqlDataSource>    

Alatta található egy GridView, amit szintén egy SqlDataSource segítségével töltök fel:

    <asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="True" 
        DataSourceID="sdsCustomers" EmptyDataText="Válasszon országot!" />

    <asp:SqlDataSource ID="sdsCustomers" runat="server" 
        ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" 
        SelectCommand="GetCustomersInCountries" SelectCommandType="StoredProcedure" 
        OnSelecting="sdsCustomers_Selecting">
        <SelectParameters>
            <asp:Parameter Name="SelectedCountries" />
        </SelectParameters>
    </asp:SqlDataSource>

A GridViewt a GetCustomersInCountries tárolt eljárás (lásd később) fogja feltölteni, ami egy SelectedCountries nevű paramétert vár. Ebbe szeretnénk betölteni a fenti listából kiválasztott országok neveit.

Itt rögtön újabb ékes bizonyítékát láthatjuk annak, hogy túl nagy a Microsoft: az ASP.NET csapat nem tudta, mit csinál az ADO.NET Team 🙂 Tábla típusú paraméterek használatához ugyanis egy olyan paramétert kell adnunk az SqlCommandhoz, amelyben az SqlDbType értéke SqlDbType.Structured. Az ilyen típusú paraméter értékeként pedig egy DataTable-t kell megadni, ami táblaként fog megérkezni az SQL Serverhez. Ezt az ADO.NET csapat jól kitalálta.

Azonban ASP.NET-ben a parancs paramétereinek inicializálását az SqlDataSource végzi, így neki kellene tudnunk megmondani, hogy a SelectedCountries paraméter tábla típusú. Csakhogy az asp:Parameter elemben átadható TypeCode attribútum felsorolt típusából kimaradt a Structured érték! Azaz szerintem a feladatot nem lehet deklaratívan megoldani, ami nekem személy szerint nagyon fáj 😦

Nézzük mi kell az SQL Server oldalán! Először is definiálnunk kell egy új típust. Én Itemsnek neveztem el, semmi köze nincs az országokhoz, 15 karakteres sztringekből tud akármennyit tárolni (lehetne több oszlopa is):

    -- Sajat tipus letrehozasa
    CREATE TYPE dbo.Items AS TABLE 
    (
        Item nvarchar( 15 )
    )
    GO

Ezek után létrehozhatjuk a tárolt eljárásunkat, amelynek Items típusú bemenő paramétere lesz:

    -- Tarolt eljaras letrehozasa
    CREATE PROC dbo.GetCustomersInCountries @SelectedCountries Items READONLY AS
    (
        SELECT ContactName, Country, City
        FROM Customers
        WHERE Country IN
        (
            SELECT Item FROM @SelectedCountries
        )
    )
    GO

Fontos, hogy a tábla típusú bemenő paraméter csak READONLY lehet.

Ezek után TSQL-ből ki is lehet próbálni, például így:

    -- Teszteles TSQL-bol
    DECLARE @Countries Items
    INSERT INTO @Countries ( Item )
        VALUES ( 'Argentina' ), 
               ( 'Germany' ),
               ( 'Finland' )
    SELECT * FROM @Countries
    EXEC dbo.GetCustomersInCountries @Countries

Már csak az maradt hátra, hogy CheckBoxListből kiolvassuk a beikszelt országokat és átadjuk őket a tárolt eljárásnak. Erre kiváló pillanat az SqlDataSource OnSelecting eseménye, itt ugyanis közvetlenül hozzáférünk az SqlCommandhoz:

    protected void sdsCustomers_Selecting( object sender, SqlDataSourceSelectingEventArgs e )
    {
        DataTable dt = new DataTable();
        dt.Columns.Add( "item", typeof( string ) );

        foreach( ListItem item in this.cblCountries.Items )
        {
            if( item.Selected )
            {
                dt.Rows.Add( item.Text );
            }
        }

        e.Command.Parameters[ "@SelectedCountries" ].Value = dt;
    }

Mindez akkor fog lefutni, amikor a felhasználó rákattint a Szűrés gombra:

    protected void btnFilter_Click( object sender, EventArgs e )
    {
        this.gvCustomers.DataBind();
    }

Bár SQL Server oldalon elő kell kicsit készíteni ezt a megoldást a CREATE TYPE hívásával, ami csak SQL Server 2008-on fog működni, mégis átláthatóbb, és gyanítom gyorsabb is a megoldás, mint a korábbi string összefűzős megközelítés.

A teljes forráskód letölthető innen.

 

Microsoft Spanyolviasz béta

Először is a tény: a Microsoft bejelentette a Microsoft Tag béta változatát. A Microsoft tag egy kétdimenziós vonalkód, amelybe URL-t, vCard információkat, telefonszámot vagy tetszőleges szöveget tehetünk. Az elkészített vonalkódot a mobiltelefonunkra telepített célszoftverrel, a beépített kamera segítségével ismertethetjük fel. Csakhogy aki követi a vonalkód világot annak számára ez a leírás már 1994 óta ismert, úgy hívják: QR code.

Bár kétdimenziós vonalkódból számos létezik, amennyire én tudom, egyik sem terjedt el consumer szinten világszerte. Ami talán kiemelkedik a többi közül az a QR Code (Quick Response), amely Japánban hódít, szinte nincs már plakát vagy hirdetés nélküle:

 QR_Code_jp_ad QR_Code_mini_ad QR_Code_ch_ad QR_Code_TheSun

Persze vannak extrém felhasználók is, már ruhára, sőt sírkőre is lehet kérni:

QR Code_add_to_friends  QR_Code_jp_grave

A Microsoft is rájött, hogy ez jó dolog, néhány évvel ezelőtt elindította a QR kódra épülő Windows Live Barcode szolgáltatást. Ez úgy tűnik nyom nélkül megszűnt, sehol nem találom…

Megjelent viszont helyette a Microsoft Tag, amiben két nagy újdonság van:

Az egyik, hogy a vonalkód nem fekete-fehér négyzetekre, hanem színes háromszögekre épül. Ez nem tűnik nagy durranásnak, sőt megnövelheti a használat költségeit nyomtatott esetekben (nem elég a fekete-fehér nyomat), elméletileg lényegesen több információ tárolását teszi lehetővé:

Kód kapacitás

Az elvet egyébként a Microsoft Research dolgozta ki High Capacity Color Barcode (HCCB) néven.

A másik nagy újdonság, hogy a vonalkódban csak egy egyedi azonosító tárolódik, amit a felismerő szoftver elküld a Microsoft internetes szolgáltatásának, ami az azonosító alapján adja vissza a kód jelentését. Ennek persze a felhasználó számára óriási hátránya, hogy a rendszer csak online módon működik – aminek a mobil szolgáltatók persze örülni fognak. A kód tulajdonosa számára viszont óriási lehetőség, hogy egy szerveren fut keresztül a felismerés, ugyanis lehet mérni a forgalmat (még location információkat is képes küldeni a kliens) vagy akár egy idő után le is járhat (expire) egy kód. A Nagy Testvér tehát figyel…

Az, hogy a kódban csak egy azonosító van, elméletileg lehetővé teszi hosszabb információk publikálását, nem beszélve arról, hogy a vonalkód fizikai mérete igen kicsi marad.

A ráadás pedig, hogy a Microsoft kilépett a saját világából és a felismerő szoftvert Windows Mobile mellett elkészítette Android, Blackberry, J2ME, PalmOS, Symbian, sőt még iPhone platformra is. Az alkalmazások a http://gettag.mobi oldalról tölthetőek le, itt akár ki is lehet próbálni őket:

Microsoft Tag: www.msdnkk.hu

Mivel a szolgáltatás ingyenes, azt hiszem minden adott a technológia elterjedéséhez. Az más kérdés, hogy vajon mennyivel járt volna rosszabbul a világ, ha a Microsoft a QR kód pályára áll rá és nem valami teljesen egyedivel áll elő…

Technorati Tags: ,,

Keresőbarát URL-ek IIS 7 alatt

Korábban már írtam az IIS 7-ben található request filtering komponensről, amely segítségével a bejövő URL-eket lehet szétszabdalni és bizonyos szempontok szerint engedélyezni vagy tiltani a kérés feldolgozását. Van egy másik eset, amikor a bejövő URL-t kell felparszolni, ez pedig virtuális URL-ek használatakor fordul elő. Vagyis amikor article.aspx?id=123 helyett barátságosabb, például article/123 formátumú címet szeretnénk használni. Erre szolgál az IIS 7-hez letölthető URL Rewrite Module komponens is.

A funkció egyébként nem új, az Apache mod_rewrite komponense már régóta tudja, de IIS alatt eddig csak fizetős komponenssel vagy egy kis ASP.NET programozással lehetett ezt a feladatot megoldani. ASP.NET-ben írtuk meg egyébként a korábbi devPortal és az MSDN Kompetencia Központ adatbázis alapú virtuális URL mappingjét is…

Szerencsére most már mindez megy kódolás nélkül is, csak le kell hozzá tölteni az ingyenes kiegészítést, ami valószínűleg az SP2-től már az IIS 7 része lesz. Az URL Rewrite modul segítségével a bejövő kérésnek gyakorlatilag bármely részére megfogalmazhatunk illeszkedési feltételt: az URL-re, a HTTP fejléc mezőkre, a query stringre vagy akár szerver változókra. Az illeszkedési feltételt megadhatjuk egyszerű wildcardos vagy reguláis kifejezés formájában. Ha a modulnak sikerül az általunk megadott szabályt ráillesztenie egy bejövő kérésre, a szabályban megadott címre irányítja át a kérést.

A dologban nem csak az a szép, hogy minden beállításunk a web.configban együtt utazik az alkalmazással, hanem az is, hogy az IIS Managerben a modulhoz tartozó grafikus felületen számos segítséget kapunk a szabályok szerkesztéséhez. Például sablonok és tesztelési felület is rendelkezésre áll.

Például valószínűleg sokan írtunk már le valami hasonlót:

    <asp:HyperLink runat="server" 
Text='<%# Eval("Title") %>' NavigateUrl='<%# Eval("ID", "Article.aspx?id={0}") %>' />

Sokkal szebb lenne, ha ezt írnánk:

    <asp:HyperLink runat="server" 
Text='<%# Eval("Title") %>' NavigateUrl='<%# Eval("ID", "Article/{0}") %>' />

Ahhoz, hogy ez működjön, persze kell egy kicsit varázsolni az IIS Managerben. Az URL Rewrite modulban kattintsunk az Add Rules… gombra, majd válasszuk a User friendly URL sablont: URL Rewrite: új szabály létrehozása

Adjuk meg azt az URL-t, amivel most működik az alkalmazásunk és válasszuk ki azt, ahogy a felhasználóknak láttatni szeretnénk a címet. Itt érdemes arra figyelni, hogy bár címként a szerver nevét is meg kell adni, az illesztési mintában és az átírt címben erre nincs szükség. Szintén érdemes kihagyni az alkalmazásunk virtuális mappáját.

URL Rewrite: új szabály létrehozása varázslóval

Ha az alsó Create corresponding redirect rule jelölőnégyzetet bekattintjuk, akkor a nem kívánatos query stringes címről is át lesznek irányítva a kérések a barátságos címre.

A varázsló leokézása után a megjelenő új szabályra duplán kattintva módosíthatjuk azt:

URL Rewrite: szabály szerkesztéseJó tudni, hogy itt válthatunk át reguláris kifejezésekről egyszerű wildcardos keresésre, illetve itt található a Test pattern funkció is, ahol kipróbálhatjuk, hogy a mintánk jól működik-e az általunk megadott URL-re.

A sok kattintgatás eredménye a web.configban található:

    <system.webServer>
        <rewrite>
            <rules>
                <clear />
                <rule name="RewriteUserFriendlyURL1" stopProcessing="true">
                    <match url="^article/([^/]+)/?$" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="article.aspx?id={R:1}" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>

Ennyi az egész, máris működik! Persze csak akkor, ha mindent jól csináltunk. Ha nem, akkor 404 – Not Found hibaüzenetet fogunk kapni. Akkor nincs más hátra, elő kell kapnunk az IIS 7 Failed Request Tracing szolgáltatását és meg kell néznünk, hogy az URL Rewrite modul működik-e vagy sem. Vegyünk fel egy új tracing szabályt a 404-es hibakódra és a trace providerek közül válasszuk ki a Rewrite komponents:

URL Rewrite: failed request tracing

Kérjük le az oldalt, majd nézzük meg a logot a C:inetpublogsFailedReqLogFilesW3SVC1 mappában:

URL Rewrite: failed request tracing log

A PATTERN_MATCH és RULE_EVALUATION_END szekciókban figyeljük a Matched és a Succeeded értékeket. Ha false, akkor bizony valamit nem jól csináltunk. Én így jöttem rá, hogy az alkalmazás mappájának nevére (a fenti képen UrlRewriteSample) nincs szükség…

Ez persze csak egy alkalmazása az URL Rewrite modulnak…

A cikkhez tartozó példa kód letölthető innen.

 

Az IIS log kigazolása

Jól működő weboldal esetén az ember ritkán nézi a webszerver naplóját, hiszen a durva hibák úgyis megjelennek a Windows eseménynaplójában, a forgalmi statisztikákat pedig a Google Analytics adja közvetlenül. Ha viszont mégis bele kell kukkantani, jön az elszörnyedés, mi ez a sok szemét és hova bújt a lényeg? Íme néhány tipp a webkiszolgáló naplójának tisztán tartásához.

Aranyszabály: amire soha nem leszünk kíváncsiak, azt ne naplózzuk!

A gyakorlatban ez általában azt jelenti, hogy nem érdekelnek a .jpg, .gif., .png, .js és .css fájlokra érkező HTTP kérések. Érdekelnek a letöltések, az oldal lekérések, hogy a böngészők megtalálták-e a favicon.ico fájlunkat, de képek, szkriptek és stíluslapok lekérését még soha nem akartam visszakeresni. Egy fotóblog vagy galéria oldalnál biztosan akarnám, olyanom viszont nincs.

A naplózást IIS6-ban ki lehet kapcsolni a Log visits kapcsolóval a webkiszolgáló minden szintjén, a beállítás öröklődik lefelé:

IIS 6: Log visits

IIS 7 esetén azonban a grafikus felületen nem fogunk ilyen nevű kapcsolót találni. Sőt, amikor először megnézzük egy mappa vagy webhely Logging beállításait minden vezérlő kikapcsolt állapotban lesz, a lényeg azonban ott virít jobb oldalon:

IIS 7: Disable logging

Ha itt kikapcsoljuk a loggolást, ez fog beíródni az applicationHost.config fájlba:

    <location path="Default Web Site/images">
        <system.webServer>
            <httpLogging dontLog="true" />
        </system.webServer>
    </location>

Látható, hogy a lényeg a httpLogging elem, a célra tartást pedig a location elemmel végezhetjük el. Ha már ezt tudjuk, takarítsuk el az ASP.NET .axd handlereit a logból. Írjuk például ezt a saját alkalmazásunk gyökér web.configjába:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>

        <!-- Szokásos részek... -->

        <location path="ScriptResource.axd">
            <system.webServer>
                <httpLogging dontLog="true" />
            </system.webServer>
        </location>
        
        <location path="WebResource.axd">
            <system.webServer>
                <httpLogging dontLog="true" />
            </system.webServer>
        </location>
    </configuration>

Megsúgom, ebből baj lesz. Mégpedig azért, mert a httpLogging szekció alapértelmezés szerint nincs delegálva, tehát csak az applicationHost.config fájlban szerkeszthető. A jutalmunk egy szép HTTP Error 500.19:

The requested page cannot be accessed because the related configuration data for the page is invalid.

This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false".

Ha hasonlóan a képek naplózását tiltjuk le, azt fogjuk tapasztalni, hogy az oldalaink betöltődnek, csak éppen a képek fognak hiányozni róluk.

Ha jól végiggondoltuk, hogy ezt a lehetőséget inkább a webhely gazdájának kezébe adnánk, irány a Feature Delegation az IIS Managerben, billentsük át a Logging sort Read/Write-ra:

IIS 7: Logging feature delegation

Ettől kezdve location tag nélkül is tehetünk web.config fájlokat például a css vagy images mappáinkba.

Sajnos a location nem ismerni a wildcardokat, nem tudjuk tehát egyszerűen kizárni a webhelyünkhöz tartozó összes .jpg fájlt. Ezért érdemes úgy szervezni a fájljainkat, hogy a naplózásból tiltandó fájlok egy mappában vagy annak almappáiban legyenek.

Az IIS 7-ben megtehetjük azt is, hogy csak a sikertelen kéréseket naplóztatjuk, azaz ahol a HTTP status code 400 vagy nagyobb. Ehhez a httpLogging szekcióban használjuk a selectiveLogging=”LogError” attribútumot.