Ošetření chyb v ASP.NET a IIS7, využití ELMAH

Jan Holan       12. 8. 2013       ASP.NET WebForms, ASP.NET/IIS       5987 zobrazení

Před nějakou dobou jsem zde uvedl řešení ošetření chyb vlastní chybovou stránkou a ošetření neexistující URL stránkou NotFound (404) v ASP.NET a IIS 7. Popis byl jako součást ukázkové ASP.NET aplikace FileAccessWeb v článku ASP.NET FileAccessWeb Sample, část 4: Ošetření chyb.

Protože požadavky, zkušenosti i technologie ASP.NET se vyvíjí, přináším zde nyní řešení nové. Změna spočívá v tom, že přechod na chybovou stránku byl původně řešen redirectem (stránka měla svojí vlastní URL). Nové řešení dodrží původní URL stránky, na které chyba vznikla, tj. zobrazí se pouze jiný obsah stránky.

Tak jdeme na to. V ASP.NET aplikaci, budu předpokládat vytvořené WebForms stránky Error.aspx a NotFound.aspx, například takto (jejich obsah ale bude závislý na konkrétní aplikaci):

Error.aspx:

<%@ Page Title="Chyba" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Error.aspx.cs" Inherits="HandleErrorWeb.Error" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
    <h1><%: Title %></h1>
    <div class="alert alert-error">
        Nastala nedefinovaná chyba. Věříme, že chyba je pouze dočasná.<br />
        V opačném případě nás prosím kontaktujte na e-mailu <a href="mailto:">webmaster</a>.
    </div>
</asp:Content>

NotFound.aspx:

<%@ Page Title="404 - Stránka nenalezena" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="NotFound.aspx.cs" Inherits="HandleErrorWeb.NotFound" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
    <h1><%: Title %></h1>
    <div class="alert alert-error">
        Litujeme, ale stránka, kterou jste si chtěli prohlédnout, neexistuje, nebo je dočasně nedostupná.<br />
        Pokud by problém přetrvával, kontaktujte nás na e-mailu <a href="mailto:">webmaster</a>.
    </div>
    <p>Přejít na <a runat="server" href="~/">úvodní stránku</a>.</p>
</asp:Content>

NotFound.aspx.cs:

protected void Page_Load(object sender, EventArgs e)
{
    this.Response.StatusCode = 404;
    this.Response.StatusDescription = "Not Found";
}

Dále v aplikaci budeme potřebovat definované routy, předpokládám routu na nějakou výchozí stránku a na stránku NotFound.aspx:

//Register routes
var routes = RouteTable.Routes;
routes.MapPageRoute("Default", "", "~/Default.aspx");
routes.MapPageRoute("NotFound", "not-found", "~/NotFound.aspx");

(Na stránku Error.aspx routu potřebovat již nebudeme).

Web.config

Nyní provedeme potřebné nastavení v konfiguračním souboru Web.config jak pro ASP.NET tak IIS 7:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />

    <customErrors mode="Off">
      <error statusCode="404" redirect="~/not-found" />
    </customErrors>
  </system.web>

  <system.webServer>
    <handlers>
      <add name="AspxBlockHandler" path="*.aspx" verb="*" type="IMP.Web.NotFoundHandler" />
    </handlers>

    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/not-found" responseMode="ExecuteURL" prefixLanguageFilePath="" />
    </httpErrors>
  </system.webServer>
</configuration>

V sekci system.web je element customErrors, u něj ovšem neuvádíme žádný odkaz na Error stránku (tedy bez atributu defaultRedirect), to zařídíme jinak později. Další nastavení stránky NotFound (v customErrors a httpErrors) a blokování aspx stránek (NotFoundHandler) zůstává stejné jako minule (*).

Výše uvedené nastavení můžeme nechat ve výchozím configu, jediné co budeme měnit v Release configu je nastavení customErrors mode="On" (pro debug budeme zobrazovat standardní ASP.NET stránku):

<system.web>
  <compilation xdt:Transform="RemoveAttributes(debug)" />
  <customErrors mode="On" xdt:Transform="SetAttributes(mode)" />
</system.web>

Zobrazení obsahu chybové stránky

Obsah vlastní chybové stránky zobrazíme v obsluze chyb Application_Error v Global.asax.cs pomoci kódu. Konkrétně k tomu využijeme metodu HttpServerUtility.Transfer, které předáme zdroj stránky načtený pomoci GetCompiledPageInstance.

protected void Application_Error(object sender, EventArgs e)
{
    var ex = Server.GetLastError();
    if (ex == null)
    {
        return;
    }

    var httpex = ex as HttpException;
    if (httpex != null && httpex.GetHttpCode() == 404)    //404 - file not found
    {
        return;
    }

    //Get the customErrors section.
    var customErrorsSection = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
    if (customErrorsSection == null || customErrorsSection.Mode == CustomErrorsMode.Off)
    {
        //Výchozí ASP.NET zobrazení chyby (při CustomErrorsMode=Off zobrazí a zaloguje ASP.NET error jinak provede zobrazení error page)
        return;
    }

    //Transfer content to Error page
    var errorPageHandler = System.Web.UI.PageParser.GetCompiledPageInstance("~/error.aspx", HttpContext.Current.Server.MapPath("~/error.aspx"), HttpContext.Current);
    Server.Transfer(errorPageHandler, true);
}

Kód ošetřuje volání pouze pokud je ve Web.config nastaveno customErrors mode="On". Také si všimněte, že vzniklá Exception není v tomto kódu ošetřovaná (výjimka se šíří dál a může být dále zalogována).

Nyní po vyvolání chyby URL stránky zůstává nezměněná

image

ELMAH

V minulém řešení kód události Application_Error obsahoval vlastní logování například do eventlogu. To nemusí být někdy vhodné, například pro aplikace nasazované na webové hostingy. Dobrou možností pro logování chyb je použití knihovny ELMAH (Error Logging Modules and Handlers). Ta zařídí nejen vlastní logování chyb (do databáze aplikace), ale obsahuje rovnou i prostředky pro jejích zobrazení v rámci naší aplikace (například pro administrátora).

Knihovnu do projektu přidáme pomoci NuGet balíčku ELMAN (PM> Install-Package elmah) nebo ELMAH on MS SQL Server (PM> Install-Package elmah.sqlserver). Balíček pro SQL server obsahuje navíc soubor Elmah.SqlServer.sql v adresáři App_Readme, tento script spustíme nad databází naší aplikace (ten vytvoří tabulku s názvem ELMAH_Error). Pro SQL server dále ještě musíme mít ve web.config nastavenou sekci connectionStrings a ELMAH na tento connection string nastavit:

<elmah>
  ...
  <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ConnectionString" />
</elmah>

Pro přístup k zalogovaným chybám slouží odkaz ~/elmah.axd, ten je ve výchozím nastavení povolen pouze pro localhost.

image

Pro povolení i vzdáleného přístupu musíme v sekci elmah nastavit:

<security allowRemoteAccess="true" />

a dále přístup ke stránce zabezpečit například jen pro administrátory (pokud v aplikaci používáme nějaké přihlašování):

<location path="elmah.axd" inheritInChildApplications="false">
  <system.web>
    <authorization> <allow roles="Admin" /> <deny users="*" /> </authorization>
  </system.web>
  <system.webServer>
    <handlers>
      <add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
    </handlers>
  </system.webServer>
</location>

Kompletní zdrojové soubory ukázkového testovacího projektu jsou k dispozici ke stažení zde.


(*) Stránky aplikace se vyvolávají přes URL definovaných pomoci rout (Routes). Zároveň chceme, aby když se uživatel zadanou URL “trefí” na fyzický soubor stránky (tj. soubor s příponou .aspx), bude tato URL zablokovaná. Možná znáte, že na to lze použít vestavěný System.Web.HttpNotFoundHandler, s ním ale pak nechodí zobrazení vlastní NotFound 404 stránky, proto používám vlastní handler NotFoundHandler.

V souboru Web.config je v customErrors a httpErrors pro odkaz na NotFound 404 stránku použita routa, protože odkaz musí být bez .aspx (které jsou blokovány). U httpErrors zde cesta musí být uvedena relativní k celé website, nelze zde použít ASP.NET relativní adresu s ~/.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback