C# - Globális hibakezelés

forráskód letöltése
A HttpApplication osztály Error eseménye, akkor kerül meghívásra, amikor webes alkalmazásunk bármely pontján olyan hiba keletkezik, mely nem lett kezelve. Ekkor ezt itt még megtehetjük és így saját, egyedi hibaüzenetet adhatunk a felhasználónk számára és nem az alapértelmezett angol nyelvű szöveg jelenik meg. Ha megpróbáljuk ezen a ponton meghatározni, hogy pontosan milyen jellegű hiba is történt és hol, akkor azt fogjuk tapasztalni, hogy nem azt az Exception osztályt kapjuk, melyben ez van leírva.
E cikkben annak járunk utána, hogy miként lehet megszerezni a tényleges hibát leíró Exception-t és hogyan kezelhetjük azt.
Mellékelt példa megnyitása előtt szükséges egy ApplicationError nevű virtuális könyvtár létrehozása, mely a példa könyvtárára mutat. Ehhez nyissa meg a mellékelt mappa Tulajdonság ablakát és itt a Webmegosztás lapon engedélyezze a mappa megosztását olvasási és parancsfájlok futtatási jogával.
A Global.asax.cs-ben lévő Application_Error függvény van hozzárendelve a HttpApplication osztály Error eseményéhez.
Ha alkalmazásunkon belül létrejön egy hiba és azt ezen a ponton szeretnénk kezelni, akkor egy System.Web.HttpUnhandledException hibát kapunk, függetlenül a tényleges hiba típusától. Nézzük miként juthatunk el az eredeti hibát leíró Exception osztályhoz.
Mindenek előtt hozzunk létre egy hibát. Legegyszerűbb megoldás erre a nullával való osztás a WebForm1-en:
    private void Button1_Click(object sender, System.EventArgs e)
    {
      int a = 5;
      int b = 0;
      Button1.Text = (a / b).ToString(); 
    }
A hiba tehát itt keletkezik, de mivel nincs kezelve ezért a Global.asax.cs-ben lévő Application_Error függvénynél folytatódik az alkalmazásunk.
Itt a megtörtént utolsó hibát a GetLastError függvénnyel kérdezhetjük le. Ekkor áll elő az a helyzet, hogy System.Web.HttpUnhandledException hibát kapjuk meg és nem a nullával való osztást.
Ha ekkor nem tennénk semmit, csak lekérdeznénk a hibát és feldolgoznánk azt, akkor megláthatnánk, hogy nem a tényleges hibáról kapunk információkat.
    protected void Application_Error(Object sender, EventArgs e)
    {
/*
      Exception lastError = Server.GetLastError();
      Server.ClearError();
      Session.Add("error", lastError);
      Response.Redirect("Error.aspx");
*/
Hogy ezt elkerüljük, fel kell használnunk azt, hogy minden hibába (Exception) beágyazható egy másik és ez folytatódhat tetszőleges szintig. Ha az első ponton keletkezett hibára vagyunk kíváncsiak, akkor addig kell visszafelé lépkednünk e listán, amíg csak találunk új elemet. Az Exception osztály tartalmaz egy InnerException property-t, melyben elérhető a beágyazott hiba.
Ennek tudatában egy ciklus segítségével visszalépkedhetünk az első hibáig, vagyis addig a pontig, amíg az InnerException property értéke már null lesz. Ekkor biztosak lehetünk abba, hogy eljutottunk az első hibát leíró Exception osztályhoz. Mely jelen esetben a nullával való osztást írja le.
      Exception innerLastError = new Exception();
      Exception lastError = Server.GetLastError();
      while (lastError != null)
      {        
        innerLastError = lastError;
        lastError = lastError.InnerException;
      }                    
A ClearError függvény hívással töröljük a hibát, annak érdekében, hogy a rendszerünk azt ne kezelje le automatikusan.
      Server.ClearError();
A hibát leíró Exception osztályt a Session-ra helyezzük, hogy azt az Error.aspx-ben tudjuk feldolgozni.
      Session.Add("error", innerLastError);
Most átirányítjuk a felhasználónkat az Error.aspx lapra, mely lehet alkalmazásunk globális hibakezelő lapja, mely minden hibánál megjelenik.
      Response.Redirect("Error.aspx");
    }
Az Error.aspx lapon tetszőleges hibakezelést oldhatunk meg igény szerint. Az egyszerűség kedvéért most csak megjelenítjük szövegesen az Exception osztályban leírtakat.
Ehhez a Session-ról kiolvassuk a hibát leíró Exception osztályt, majd rögtön töröljük is onnan.
    private void Page_Load(object sender, System.EventArgs e)
    {
      Exception error = (Exception)Session["error"];
      if (error != null)
      {
A Label1-en jelenítjük meg a hiba eredeti leírását.
        Label1.Text = error.Message + "<br><br>" + error.Source + "<br><br>" + error.StackTrace;
        Session.Remove("error");
      }
    }
Jól megfigyelhető, hogy a megjelenő hibaüzenet már valóban a nullával történő osztásról szól (Attempted to divide by zero), megjelelve annak a forráskódnak a pontos helyét ahol ténylegesen bekövetkezett.