2015. december 17., csütörtök

Fájlgenerálás végeztének kezelése JS-ben

Előfordulnak olyan helyzetek, mikor egy online felületen, egy formon megadhatunk keresési paramétereket, majd az eredményt rögtön letöltve szeretnénk megkapni. Ilyenkor célszerű addig letiltani gombot, amíg dolgozik a rendszer. Ezzel elkerülhető, hogy a hosszú várakozási idő alatt többször rányomjon a felhasználó. Hiszen az ő szemében "már bezárta a böngészőt, tehát befejezte a műveletet", attól a szerver még végigcsinálja a feladatot.

JavaScriptben nincs egységes módszer, vagy esemény, vagy bármi arra, hogy mikor mondhatnánk azt, hogy most már lehet engedélyezni a gombot. Viszont van egy elfogadhatóan jó kerülő megoldás: tegyünk bele a sütibe egy tokent, amikor a kiszolgáló visszaküldi a választ és benne a letöltendő fájlt, és figyeljük JS-ből ezt a tokent, hogy elérhető-e már.

Az alábbi linken a UI blokkolásához a JQuery Block UI plugint használják, ami természetesen cserélhető/kihagyható az aktuális feladattól függően. A linken üríti a sütit egy külső JQuery pluginnel, ami tartalmazza a $.cookie-t. Erre azonban nincs szükség, hiszen minden gombnyomásnál új tokent generálunk és addig vizsgálódunk, amíg meg nem érkezik a token. Emiatt nem gond, hogy ha benne marad a token a régi értékkel olyankor, amikor amúgy sem várunk semmire sem.



http://gruffcode.com/2010/10/28/detecting-the-file-download-dialog-in-the-browser/

Using és a WCF kliens

Minden IDisposable implementációnál szükséges meghívni a Dispose metódust, hogy felszabadítsuk az erőforrásokat. Kézenfekvő megoldás rá a using blokk használata, mivel egy "finally" ágban automatikusan meghívja. Azonban egy WCF kliensnél a Dispose hívás egyenértékű azzal, hogy a csatornára hívunk egy Close-t. Ez viszont hibát dobhat, ha a csatorna Faulted állapotban van, mivel olyankor Abortot kellene hívni. Bővebb leírást egy MSDN bejegyzés tartalmaz.

Szerencsére az IDisposable.Dispose virtuális, így felül lehet definiálni. Mivel a generált WCF kliens osztály partial class, így könnyedén elérhető, hogy a using Dispose hívása ne dobjon hibát. Ennek eredménye, hogy ténylegesen a Faulted állapotot kiváltó hibaüzenetet fogjuk megkapni.

Az alábbi partial class-t kell létrehozni, ami nyugodtan implementálhatja az IDisposable interfészt, mivel a ClientBase nem teszi.

public partial class CalculatorClient : IDisposable
{
    #region IDisposable implementation
    void IDisposable.Dispose()
    {
        Dispose(true)
    }
 
    /// <summary>
    /// Dispose hívás. 
    /// Értelmesen kezeli a lezárást akkor is, ha a faulted állapotban van.
    /// </summary>
    /// <param name="disposing">Dispose? (másik lehetőség a Finalizing)</param>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            try
            {
                if (State != CommunicationState.Faulted)
                {
                    Close();
                }
            }
            finally
            {
                if (State != CommunicationState.Closed)
                {
                    Abort();
                }
            }
        }
    }
 
    ~CalculatorClient()
    {
        Dispose(false);
    }
 
    #endregion
}

Ehhez továbbá engedélyezni kell a service app.configjában lévő megfelelő ServiceBehavior szekcióban is, hogy benne legyen a hibaüzenet a válaszban. Enélkül csak olyan hibaüzenet jelenik meg, hogy nem sikerült feldolgozni a hibát, mivel nincs bekapcsolva, hogy legyen benne a válaszban.

<serviceBehaviors>
   <behavior>
     <serviceDebug includeExceptionDetailInFaults="true"/>
   </behavior>
</serviceBehaviors>

MONO és a lezáró nulla

Gyakori feladat, hogy egy .NET C# NetworkStreamből kell kiolvasni a benne rejlő adatot a NetworkStream.Read(byte[] buffer, int offset, int size):int metódussal.

Az alábbi linken van hozzá egy jó módszer, miként célszerű használni:
https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.dataavailable(v=vs.110).aspx

Fontos odafigyelni a Read hívásánál, hogy a visszatérési értékben kapjuk meg, hogy hány bájt volt a ténylegesen kiolvasott adat. Ne akarjunk a pufferből annál több elemet kiolvasni, a stringgé alakításkor a többi karakter a '\0' értéket fogja felvenni.

Ha mégis megfeledkeznénk erről és a teljes puffer tartalmát használjuk, akkor sem fog hibát dobni. Viszont létezik olyan .NET C# implementáció, mint a MONO, amiben a '\0' a null-terminator, azaz a stringet "lezáró nulla", emiatt pedig mindent eldob, ami utána van.

Ez a viselkedés olyan értelmetlennek tűnő szituációkat tud teremteni, mint hogy az így előállított stringhez a konkatenációval nem lehet semmit sem hozzáfűzni, és még hibát sem dob.

Bővebben a C++ lezáró nulla karakterről:
https://msdn.microsoft.com/en-us/library/69ze775t.aspx

HTTPS-re átirányítás, Form authentication és duplázódott URL paraméterek

Rendszeresen előfordult, hogy a felparaméterezett linkre bejelentkezést követően hibát dobott az ASP.NET codebehind, mondván, hogy nem megfelelő egy vagy több paraméter formátuma. A belépést követően megnézve a linket jogos volt a hibaüzenet, mivel duplázódott mindegyik paraméter, emiatt a QueryString vesszővel összefűzve adta vissza őket.

Ezt a duplázódást a Form authentication csinálta. Védett tartalom elérésekor átirányít a beléptető oldalra, és közben elteszi egy ReturnUrl paraméterbe az eredeti link URL kódolt változatát. Azért, hogy a beléptető oldalon is elérhetők legyenek a paraméterek anélkül, hogy az egész linket fel kéne dolgozni, melléteszi külön a paramétereket is, Ennek hatására a paraméterek kétszer fognak szerepelni: URL kódolva és anélkül.  

A probléma ott folytatódott, hogy nem volt a ReturnUrl tartalma URL kódolva. Kiderült, hogy a HTTP kérések HTTPS-re való átirányítása hibásan történt az ősosztály Page.OnLoadjában.

Az alábbi kód bár kézen fekvőnek tűnik, nem az elvártnak megfelelően működik, az URI.ToString() dekódolva adja vissza az URL-t:
Response.Redirect(Request.Url.ToString().Insert(4,"s"))

Egy lehetséges megoldás a helyes átirányításra:
Request.Url.Scheme + "s://" + Request.Url.Authority + Request.RawUrl;

A HTTPS-re való átirányítás után így már megmaradt a helyesen kódolt URL, azaz a ReturnUrl-ben benne volt a teljes eredeti hivatkozás és a Form authentication által hozzáfűzött paraméterlista is elérhető volt. Ennek ellenére a sikeres belépést követően mégis benne maradt az URL-ben a duplázott paraméterlista.

A user validálás után volt a másik hiba: az URL egyszerűen szét volt bontva a ReturnUrl mentén. Nem csak a ReturnUrl tartalmát adta vissza, hanem mindent, ami utána volt, így a duplázott paraméterlistát is.


--
A Form authenticationről bővebben:
http://blogs.msdn.com/b/vijaysk/archive/2008/01/24/anatomy-of-forms-authentication-return-url.aspx

SSRS - hiányzó nullozható paraméter

Amikor már nem először deploy-olunk egy riportot, akkor a már létező paraméterek beállításai nem frissülnek. Stackoverflow magyarázatok szerint cache probléma, míg az MSDN szerint (egy hasonló reporting service-re hivatkozva) alapján az van a háttérben, hogy a Report Manager oldalon esetlegesen eszközölt módosításokat nem akarja a rendszer felülírni.

Abban viszont egyet értenek, hogy trükközve, de megoldható a probléma:

Módszer 1:
A Report Manager oldalon módosítjuk a paraméter beállításait

Módszer 2:
Töröljük a kitett riportot és tegyük ki ismét.

Módszer 3:
- nevezzük át a paramétert, és állítsuk be a kívánt dolgokat
- deploy
- nevezzük vissza
- deploy



XAML Binding: Path és ElementName

Ha WPF XAML-ben valami másik objektum tulajdonságához szeretnénk bindolni, akkor ezt könnyedén megtehetjük a ElementName paraméter beállításával.
FONTOS, hogy ebben az esetben a hivatkozott property útvonalát is módosítani kell, elé kell tenni, hogy DataContext. Ekkor fogja megtalálni a célobjektum általunk keresett property-jét.

Azaz például:
{Binding Path=DataContext.DesiredProperty, ElementName=target}

Amennyiben megfeledkezünk arról, hogy az ElementName használatával a célobjektum DataContext property-jében kell keresnünk a saját modellünket, akkor komoly problémát fog okozni az, hogy semmilyen hibát nem kapunk majd, csak nem fog működni. Lefordul és fut hiba nélkül, pusztán nem működik.