ASP.NET FileAccessWeb Sample, část 4: Ošetření chyb

Jan Holan       25. 7. 2012       ASP.NET/IIS       5758 zobrazení

Update:
Novější ošetření vlastní chybové stránky je v článku Ošetření chyb v ASP.NET a IIS7, využití ELMAH

Minule jsme skoro dokončili naší ukázkovou ASP.NET aplikaci pro přístup k souborům (její kompletní zdrojové soubory jsou k dispozici zde). V této poslední části doplníme ještě ošetření případných chyb aplikace a vytvoříme vlastní stránky pro Error a 404 (NotFound).

Celé si to rozdělíme celkem do tří částí. V první části naimplementujeme kód pro zalogování chyby, v druhé části vytvoříme stránku, která bude sloužit k tomu, aby uživatel chybový výpis neviděl, místo toho mu zobrazíme pouze informaci, že k chybě došlo. V poslední části si vysvětlíme a provedeme potřebná nastavení pro vlastní stránku 404 – Stránka nenalezena. Také při tvorbě těchto stránek budeme brát v úvahu ošetření pro SEO.

Zalogování chyby

Do souboru Global.asax.cs přidáme následující kód události Application_Error.

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

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

        //Zalogování chyby
        PublishException(ex);

        //Get the customErrors section.
        CustomErrorsSection 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 udělá redirect na error page)
            return;
        }

        if (!string.IsNullOrEmpty(customErrorsSection.DefaultRedirect))
        {
            Server.ClearError();

            //Redirect na custom error page
            HttpContext.Current.Response.Redirect(customErrorsSection.DefaultRedirect);
        }
    }
    catch (Exception localEx)
    {
        PublishException(localEx);
    }
}

Kód získá Exception vzniklé chyby. Pokud se jedná o chybu 404 - Not found, tak je metoda ukončena, pro tuto chybu nepotřebujeme speciální obsluhu. V jiném případě je volána metoda PublishException pro zalogování chyby. Dále je načtena konfigurační sekce CustomErrors Web.config souboru, pokud je Custom error zapnuto, tak je proveden redirect na nastavenou error stránku.

Tento redirect by se po skončení metody Application_Error provedl automaticky i sám, důvod, proč je voláno ClearError a redirect volán ručně je ten, že při automatickém redirectu by ještě ASP.NET sám zalogoval chybu jeho mechanizmem do evenlogu (a to je vzhledem k vlastnímu logovacímu mechanizmu nežádoucí).

Příklad velice jednoduché metody PublishException, která zapisuje do evenlogu je zde:

#region action methods
/// <summary>
/// Zápis <paramref name="ex"/> do logu
/// </summary>
/// <param name="ex"><see cref="System.Exception"/></param>
public static void PublishException(System.Exception ex)
{
    if (ex is System.Web.HttpUnhandledException)
    {
        TryPublishException(ex.InnerException);
        return;
    }

    TryPublishException(ex);
}
#endregion

#region private member functions
private static bool TryPublishException(Exception ex)
{
    try
    {
        using (System.Security.Principal.WindowsIdentity.Impersonate(IntPtr.Zero))  //If some user is currently being impersonated, control reverts to the original user.
        {
            string sourceName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

            // Write the entry to the Event Log.
            System.Diagnostics.EventLog.WriteEntry(sourceName, ex.ToString(), System.Diagnostics.EventLogEntryType.Error);
        }

        return true;
    }
    catch
    {
        //Ignore exception
        return false;
    }
}
#endregion

V reálné aplikaci ale použijte jiný způsob, aby jste získali podrobnější chybový výpis (v tomto případě je podrobnější i již zmíněný standardní ASP.NET výpis). Zde mi jde ale o ukázání způsobu jak mít takový zápis do eventlogu ošetřen (na zápis do eventlogu nemusí být potřebná přístupová práva).

Protože se zápis provádí do aplikačního logu pod jménem (source) “FileAccessWeb” je nutné tento zdroj na webovém serveru vytvořit následujícím reg souborem CreateEventLogSource.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\FileAccessWeb]
"EventMessageFile"=hex(2):43,00,3a,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,\
  00,73,00,5c,00,4d,00,69,00,63,00,72,00,6f,00,73,00,6f,00,66,00,74,00,2e,00,\
  4e,00,45,00,54,00,5c,00,46,00,72,00,61,00,6d,00,65,00,77,00,6f,00,72,00,6b,\
  00,5c,00,76,00,34,00,2e,00,30,00,2e,00,33,00,30,00,33,00,31,00,39,00,5c,00,\
  45,00,76,00,65,00,6e,00,74,00,4c,00,6f,00,67,00,4d,00,65,00,73,00,73,00,61,\
  00,67,00,65,00,73,00,2e,00,64,00,6c,00,6c,00,00,00

Stránka Error.aspx

Nyní vytvoříme a nastavíme stránku, kterou uživatel uvidí, pokud v aplikaci k nějaké chybě dojde. Stránku nazveme Error.aspx a opět bude používat stejnou Site.Master page jako jiné stránky webu. Její HTML může vypadat zhruba následovně:

<%@ Page Title="Chyba" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Error.aspx.cs" Inherits="FileAccessWeb.Error" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
    <title>Chyba | File Access Web</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <p>
        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>.
    </p>
</asp:Content>

Dále je nutné tuto stránku nastavit v souboru Web.config do sekce system.web, kde potřebujeme vložit toto nastavení: <customErrors mode="On" defaultRedirect="~/Error"/>

V reálné situaci pro toto využijeme transformace web config souboru a toto nastavení přidáme pouze pro release. Pro vývoj pak nastavíme customErrors na Off. Proto provedeme nastavení takto:

Web.config:
  <system.web>
    ...
    <customErrors mode="Off"/>
    ...
  </system.web>

Web.Release.config:
  <system.web>
    ...
    <customErrors mode="On" defaultRedirect="~/Error" xdt:Transform="Replace">
      <error statusCode="404" redirect="~/NotFound" />
    </customErrors>
  </system.web>

Pro Error stránku ještě musíme do Application_Start v Global.asax.cs doplnit patřičnou Route registraci:

RouteTable.Routes.MapPageRoute("Error", "Error", "~/Error.aspx");

Stránku Error by vyhledávače měli ignorovat, proto do webu doplníme soubor robots.txt. Pro tento web jsem se rozhodl nastavit zakázání přístupu úplně pomoci této definice:

User-agent: * 
Disallow: / 

Na jiných webech asi spíše využijete tuto definici:

User-agent: *
Disallow: /Error

Pozn.: Opravdu se mi už stalo, že seznam.cz při indexaci webu usoudil, že výchozí stránka je právě Error.aspx, proto toto nastavení nepodceňujte.

Zbývá nám ještě doplnit povolení přístupu na tyto nově přidané soubory i pro nepřihlášeného uživatele:

<location path="Error">
  <system.web>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>
</location>
<location path="Error.aspx">
  <system.web>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>
</location>
<location path="robots.txt">
  <system.web>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>
</location>

Stránka NotFound.aspx

Třetí dnešní částí je vytvoření stránky, která bude uživateli zobrazena místo výchozí při přechodu na neexistující URL webu. Nazveme jí NotFound.aspx a bude vypadat takto:

<%@ Page Title="404 - Stránka nenalezena" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="NotFound.aspx.cs" Inherits="FileAccessWeb.NotFound" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
    <title>Stránka nenalezena | File Access Web</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <p>
        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>.
    </p>
    <p>Přejít na <asp:HyperLink runat="server" NavigateUrl="~/">úvodní stránku</asp:HyperLink>.</p>
</asp:Content>

Ačkoliv stránka zobrazuje obsah pro uživatele, z hlediska SEO by měla stránka pořád vracet hlavičku se stejným 404 chybovým statusem, to docílíme přidáním tohoto code behind:

public partial class NotFound : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        this.Response.StatusCode = 404;
        this.Response.StatusDescription = "Not Found";
    }
}

Pro stránku opět v Global.asax přidáme patřičnou routu:

RouteTable.Routes.MapPageRoute("NotFound", "NotFound", "~/NotFound.aspx");

A nakonec provedeme patřičné nastavení ve Web.config souboru. Při definici customErrors jsme již odkaz pro 404 uvedli. Ten se ale uplatní pouze na .NET stránky, pro odchycení “všech” 404 chyb slouží v IIS 7/7.5 podsekce httpErrors sekce webServer, do které je nutné naší stránku zaregistrovat takto:

<system.webServer>
  <httpErrors>
    <remove statusCode="404" subStatusCode="-1"/>
    <error statusCode="404" path="/FileAccessWeb/NotFound" responseMode="ExecuteURL" prefixLanguageFilePath="" />
  </httpErrors>
</system.webServer>

Všimněte si, že path zde musí být uvedena relativní k celé website, nelze zde použít ASP.NET relativní adresu s ~/.

Pokud toto chování zkoušíte lokálně, tak se vám nastavení neprojeví, protože ve výchozím stavu IIS pro lokální přístup zobrazí svojí chybovou stránku a detailnějším výpisem. Toto chování můžete přenastavit změnou hodnoty atributu errorMode na Custom (výchozí je DetailedLocalOnly):

<httpErrors errorMode="Custom">
  <remove statusCode="404" subStatusCode="-1"/>
  <error statusCode="404" path="/FileAccessWeb/NotFound" responseMode="ExecuteURL" prefixLanguageFilePath="" />
</httpErrors>

My ale naopak toto nastavení přidáme zase pouze do Web.Release.config s příslušnou transformací:

<system.webServer>
  <httpErrors xdt:Transform="Insert">
    <remove statusCode="404" subStatusCode="-1"/>
    <error statusCode="404" path="/FileAccessWeb/NotFound" responseMode="ExecuteURL" prefixLanguageFilePath="" />
  </httpErrors>
</system.webServer>

Podobně by jsme samozřejmě mohli dále ošetřit i jiné chybové stavy jako 401 (Unauthorized), 403 (Forbidden) nebo 500 (Internal error).

 

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