Sdílet prostřednictvím


Zpracování neošetřených výjimek (C#)

Scott Mitchell

Zobrazení nebo stažení ukázkového kódu (postup stažení)

Pokud dojde k chybě za běhu webové aplikace v produkčním prostředí, je důležité upozornit vývojáře a protokolovat chybu, aby ji bylo možné diagnostikovat později. Tento kurz poskytuje přehled o tom, jak ASP.NET zpracovává chyby modulu runtime, a dívá se na jeden ze způsobů, jak nechat spustit vlastní kód pokaždé, když se neošetřená výjimka zobrazí až do ASP.NET modulu runtime.

Úvod

Pokud v aplikaci ASP.NET dojde k neošetřené výjimce, vytvoří se bublina až do ASP.NET modulu runtime, který vyvolá Error událost a zobrazí příslušnou chybovou stránku. Existují tři různé typy chybových stránek: Žlutá obrazovka smrti (YSOD) chyby modulu runtime; podrobnosti o výjimce YSOD; a vlastní chybové stránky. V předchozím kurzu jsme aplikaci nakonfigurovali tak, aby používala vlastní chybovou stránku pro vzdálené uživatele a YSOD podrobností o výjimce pro uživatele, kteří navštíví místně.

Použití vlastní chybové stránky, která odpovídá vzhledu a chování webu, se upřednostňuje před výchozí chybou YSOD za běhu, ale zobrazení vlastní chybové stránky je pouze jednou částí komplexního řešení zpracování chyb. Pokud dojde k chybě v aplikaci v produkčním prostředí, je důležité, aby byli vývojáři na chybu upozorněni, aby mohli odhalit příčinu výjimky a vyřešit ji. Kromě toho by se měly protokolovat podrobnosti o chybě, aby bylo možné chybu později prozkoumat a diagnostikovat.

V tomto kurzu se dozvíte, jak získat přístup k podrobnostem o neošetřené výjimce, aby je bylo možné protokolovat a upozornit vývojáře. Dva následující kurzy zkoumají knihovny protokolování chyb, které po určité konfiguraci automaticky upozorňují vývojáře na chyby za běhu a protokolují jejich podrobnosti.

Poznámka

Informace zkoumané v tomto kurzu jsou nejužitečnější, pokud potřebujete zpracovávat neošetřené výjimky nějakým jedinečným nebo přizpůsobeným způsobem. V případech, kdy potřebujete jenom zaznamenat výjimku a upozornit vývojáře, je použití knihovny protokolování chyb tou nejlepší možností. Následující dva kurzy poskytují přehled dvou takových knihoven.

Spouštění kódu při vyvoláníErrorudálosti

Události poskytují objektu mechanismus pro signalizaci, že došlo k něčemu zajímavému, a pro jiný objekt spustit kód v reakci. Jako vývojář ASP.NET jste zvyklí myslet na události. Pokud chcete spustit nějaký kód, když návštěvník klikne na konkrétní tlačítko, vytvoříte obslužnou rutinu události pro danou událost Button Click a vložíte do ní kód. Vzhledem k tomu, že modul runtime ASP.NET vyvolá svou Error událost vždy, když dojde k neošetřené výjimce, znamená to, že kód pro protokolování podrobností o chybě by šel do obslužné rutiny události. Jak ale vytvořit obslužnou rutinu Error události pro událost?

Událost Error je jednou z mnoha událostí ve HttpApplication třídě , které jsou vyvolány v určitých fázích kanálu HTTP během životnosti požadavku. Například HttpApplication událost třídy BeginRequest je vyvolána na začátku každého požadavku; její AuthenticateRequest událost se vyvolá, když modul zabezpečení identifikuje žadatele. Tyto HttpApplication události dávají vývojáři stránky možnost spouštět vlastní logiku v různých bodech životnosti požadavku.

Obslužné rutiny událostí pro HttpApplication události lze umístit do speciálního souboru s názvem Global.asax. Pokud chcete vytvořit tento soubor na svém webu, přidejte novou položku do kořenového adresáře vašeho webu pomocí šablony Globální třída aplikace s názvem Global.asax.

Sceenshot, který zvýrazní soubor Globální tečka A S A X.

Obrázek 1: Přidání Global.asax do webové aplikace
(Kliknutím zobrazíte obrázek v plné velikosti.)

Obsah a struktura souboru vytvořeného Global.asax sadou Visual Studio se mírně liší v závislosti na tom, jestli používáte projekt webových aplikací (WAP) nebo projekt webu (WSP). S WAP Global.asax je implementovaný jako dva samostatné soubory – Global.asax a Global.asax.cs. Soubor Global.asax neobsahuje nic než direktivu @Application.cs , která na soubor odkazuje. V souboru jsou definovány Global.asax.cs obslužné rutiny událostí, které vás zajímají. Pro wsPs je vytvořen Global.asaxpouze jeden soubor a obslužné rutiny událostí jsou definovány v <script runat="server"> bloku.

Soubor Global.asax vytvořený ve WAP šablonou globální aplikační třídy sady Visual Studio obsahuje obslužné rutiny událostí s názvy Application_BeginRequest, Application_AuthenticateRequesta Application_Error, které jsou obslužnými rutinami událostí , AuthenticateRequestHttpApplicationBeginRequesta .Error Existují také obslužné rutiny událostí s názvem Application_Start, Session_Start, Application_Enda Session_End, což jsou obslužné rutiny událostí, které se aktivují při spuštění webové aplikace, při spuštění nové relace, při ukončení aplikace a při ukončení relace. Soubor Global.asax vytvořený v sadě Visual Studio ve WSP obsahuje pouze Application_Errorobslužné rutiny událostí , Application_StartSession_Start, Application_End, a Session_End .

Poznámka

Při nasazování aplikace ASP.NET musíte soubor zkopírovat Global.asax do produkčního prostředí. Soubor Global.asax.cs , který je vytvořen v WAP, není nutné kopírovat do produkčního prostředí, protože tento kód je zkompilován do sestavení projektu.

Obslužné rutiny událostí vytvořené šablonou Globální aplikační třídy sady Visual Studio nejsou vyčerpávající. Obslužnou rutinu události můžete přidat pro libovolnou HttpApplication událost pojmenováním obslužné rutiny Application_EventNameudálosti . Do souboru můžete například přidat následující kódGlobal.asax, který vytvoří obslužnou rutinuAuthorizeRequest události:

protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
    // Event handler code
}

Stejně tak můžete odebrat všechny obslužné rutiny událostí vytvořené šablonou Globální aplikační třídy, které nejsou potřeba. Pro účely tohoto kurzu vyžadujeme pouze obslužnou rutinu Error události. Nebojte se odebrat ostatní obslužné rutiny událostí ze Global.asax souboru.

Poznámka

Moduly HTTP nabízejí další způsob, jak definovat obslužné rutiny událostí pro HttpApplication události. Moduly HTTP jsou vytvořeny jako soubor třídy, který lze umístit přímo do projektu webové aplikace nebo oddělit do samostatné knihovny tříd. Vzhledem k tomu, že je lze oddělit do knihovny tříd, nabízejí moduly HTTP flexibilnější a opakovaně použitelný model pro vytváření HttpApplication obslužných rutin událostí. Global.asax Zatímco soubor je specifický pro webovou aplikaci, kde se nachází, moduly HTTP lze zkompilovat do sestavení. V tomto okamžiku je přidání modulu HTTP na web stejně jednoduché, jako je vyřazení sestavení ve Bin složce a registrace modulu v Web.config. Tento kurz se nezabívá vytvářením a používáním modulů HTTP, ale dvě knihovny protokolování chyb použité v následujících dvou kurzech jsou implementované jako moduly HTTP. Další informace o výhodách modulů HTTP najdete v tématu Použití modulů HTTP a obslužných rutin k vytvoření připojitelných ASP.NET komponent.

Načítání informací o neošetřené výjimce

V tomto okamžiku máme soubor Global.asax s obslužnou rutinou Application_Error události. Při spuštění této obslužné rutiny události musíme upozornit vývojáře na chybu a protokolovat její podrobnosti. Abychom mohli tyto úlohy provést, musíme nejprve určit podrobnosti o vyvolané výjimce. Pomocí metody objektu GetLastError Serveru načtěte podrobnosti o neošetřené výjimce, která způsobilaError, že se událost aktivovala.

protected void Application_Error(object sender, EventArgs e)
{
    // Get the error details
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;
}

Metoda GetLastError vrátí objekt typu Exception, což je základní typ pro všechny výjimky v rozhraní .NET Framework. Ve výše uvedeném kódu však přetypuji objekt Exception vrácený uživatelem GetLastError do objektu HttpException . Error Pokud se událost aktivuje, protože během zpracování prostředku ASP.NET došlo k výjimce, je vyvoláná výjimka zabalena do objektu HttpException. Pokud chcete získat skutečnou výjimku, která vychýlila událost Error, použijte InnerException vlastnost . Error Pokud byla událost vyvolána kvůli výjimce založené na protokolu HTTP, například požadavku na neexistující stránku, HttpException vyvolá se událost, ale nemá vnitřní výjimku.

Následující kód používá GetLastErrormessage k načtení informací o výjimce, která aktivovala Error událost, a uloží HttpException ji do proměnné s názvem lastErrorWrapper. Potom uloží typ, zprávu a trasování zásobníku původní výjimky do tří řetězcových proměnných a zkontroluje, jestli lastErrorWrapper je skutečná výjimka, která aktivovala Error událost (v případě výjimek založených na protokolu HTTP), nebo jestli se jedná pouze o obálku výjimky, která byla vyvolána při zpracování požadavku.

protected void Application_Error(object sender, EventArgs e)
{
    // Get the error details
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;

    Exception lastError = lastErrorWrapper;
    if (lastErrorWrapper.InnerException != null)
        lastError = lastErrorWrapper.InnerException;

    string lastErrorTypeName = lastError.GetType().ToString();
    string lastErrorMessage = lastError.Message;
    string lastErrorStackTrace = lastError.StackTrace;
}

V tomto okamžiku máte všechny informace potřebné k zápisu kódu, který bude protokolovat podrobnosti o výjimce do databázové tabulky. Můžete vytvořit tabulku databáze se sloupci pro jednotlivé podrobnosti o chybě, které vás zajímají – typ, zprávu, trasování zásobníku atd. – a další užitečné informace, jako je adresa URL požadované stránky a jméno aktuálně přihlášeného uživatele. V obslužné rutině Application_Error události byste se pak připojili k databázi a vložili záznam do tabulky. Podobně můžete přidat kód, který upozorní vývojáře na chybu e-mailem.

Knihovny protokolování chyb, které byly prozkoumány v následujících dvou kurzech, poskytují tyto funkce hned po dokončení, takže není nutné vytvářet protokolování chyb a oznámení sami. Abychom však ilustrovali Error , že událost je vyvolána a že obslužnou rutinu Application_Error události lze použít k protokolování podrobností o chybě a upozornění vývojáře, přidejte kód, který upozorní vývojáře, když dojde k chybě.

Upozornění vývojáře, když dojde k neošetřené výjimce

Pokud v produkčním prostředí dojde k neošetřené výjimce, je důležité upozornit vývojový tým, aby mohl chybu vyhodnotit a určit, jaké akce je potřeba provést. Pokud například dojde k chybě při připojování k databázi, budete muset pečlivě zkontrolovat připojovací řetězec a třeba otevřít lístek podpory u vaší společnosti pro hostování webů. Pokud k výjimce došlo kvůli chybě programování, může být potřeba přidat další kód nebo ověřovací logiku, aby se takovým chybám v budoucnu zabránilo.

Třídy rozhraní .NET Framework v System.Net.Mail oboru názvů usnadňují odeslání e-mailu. TřídaMailMessage představuje e-mailovou zprávu a má vlastnosti jako To, SubjectFrom, , Bodya Attachments. Slouží SmtpClass k odeslání objektu MailMessage pomocí zadaného serveru SMTP. Nastavení serveru SMTP lze zadat programově nebo deklarativně v elementu<system.net>Web.config filev . Další informace o odesílání e-mailových zpráv v aplikaci ASP.NET najdete v článku Odesílání Email z webu ASP.NET webových stránek a nejčastějších dotazech k System.Net.Mail.

Poznámka

Element <system.net> obsahuje nastavení serveru SMTP používané SmtpClient třídou při odesílání e-mailu. Vaše webhostingová společnost má pravděpodobně server SMTP, který můžete použít k odesílání e-mailů z vaší aplikace. Informace o nastavení serveru SMTP, které byste měli použít ve webové aplikaci, najdete v části podpory vašeho webového hostitele.

Přidejte do obslužné rutiny Application_Error události následující kód, který vývojáři pošle e-mail, když dojde k chybě:

void Application_Error(object sender, EventArgs e)
{
    // Get the error details
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;

    Exception lastError = lastErrorWrapper;
    if (lastErrorWrapper.InnerException != null)
        lastError = lastErrorWrapper.InnerException;

    string lastErrorTypeName = lastError.GetType().ToString();
    string lastErrorMessage = lastError.Message;
    string lastErrorStackTrace = lastError.StackTrace;

    const string ToAddress = "support@example.com";
    const string FromAddress = "support@example.com";
    const string Subject = "An Error Has Occurred!";
    
    // Create the MailMessage object
    MailMessage mm = new MailMessage(FromAddress, ToAddress);
    mm.Subject = Subject;
    mm.IsBodyHtml = true;
    mm.Priority = MailPriority.High;
    mm.Body = string.Format(@"
<html>
<body>
  <h1>An Error Has Occurred!</h1>
  <table cellpadding=""5"" cellspacing=""0"" border=""1"">
  <tr>
  <tdtext-align: right;font-weight: bold"">URL:</td>
  <td>{0}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">User:</td>
  <td>{1}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">Exception Type:</td>
  <td>{2}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">Message:</td>
  <td>{3}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">Stack Trace:</td>
  <td>{4}</td>
  </tr> 
  </table>
</body>
</html>",
        Request.RawUrl,
        User.Identity.Name,
        lastErrorTypeName,
        lastErrorMessage,
        lastErrorStackTrace.Replace(Environment.NewLine, "<br />"));

    // Attach the Yellow Screen of Death for this error   
    string YSODmarkup = lastErrorWrapper.GetHtmlErrorMessage();
    if (!string.IsNullOrEmpty(YSODmarkup))
    {
        Attachment YSOD = 
            Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.htm");
        mm.Attachments.Add(YSOD);
    }

    // Send the email
    SmtpClient smtp = new SmtpClient();
    smtp.Send(mm);
}

I když je výše uvedený kód poměrně dlouhý, jeho velká část vytvoří kód HTML, který se zobrazí v e-mailu odeslaném vývojáři. Kód začíná odkazováním na HttpException vrácené metodou GetLastError (lastErrorWrapper). Skutečná výjimka, která byla vyvolána požadavkem, se načte prostřednictvím lastErrorWrapper.InnerException a je přiřazena k proměnné lastError. Informace o trasování typu, zprávy a zásobníku se načítají ze lastError tří řetězcových proměnných a ukládají se do těchto tří proměnných.

Dále se MailMessage vytvoří objekt s názvem mm . Text e-mailu má formát HTML a zobrazuje adresu URL požadované stránky, jméno aktuálně přihlášeného uživatele a informace o výjimce (typ, zprávu a trasování zásobníku). Jednou z skvělých věcí na HttpException třídě je, že můžete vygenerovat HTML použitý k vytvoření výjimky Yellow Screen of Death (YSOD) voláním GetHtmlErrorMessage metoda. Tato metoda se zde používá k načtení značek YSOD podrobností o výjimce a přidání do e-mailu jako přílohy. Jedno upozornění: Pokud výjimka, která aktivovala Error událost, byla výjimka založená na protokolu HTTP (například požadavek na neexistující stránku), GetHtmlErrorMessage pak metoda vrátí null.

Posledním krokem je odeslání příkazu MailMessage. To se provádí vytvořením nové SmtpClient metody a voláním její Send metody.

Poznámka

Před použitím tohoto kódu ve webové aplikaci budete chtít změnit hodnoty v ToAddress konstantách support@example.com a FromAddress na libovolnou e-mailovou adresu, na kterou by měl být e-mail s oznámením o chybě odeslán a ze které má pocházet. Budete také muset zadat nastavení serveru SMTP v <system.net> části v Web.config. Obraťte se na poskytovatele webového hostitele a určete nastavení serveru SMTP, které se má použít.

S tímto kódem na místě, kdykoli dojde k chybě, vývojáři pošle e-mailovou zprávu, která shrnuje chybu a obsahuje YSOD. V předchozím kurzu jsme ukázali chybu za běhu tím, že jsme navštívili Genre.aspx a předali neplatnou ID hodnotu přes řetězec dotazů, například Genre.aspx?ID=foo. Při návštěvě stránky se souborem Global.asax vygenerujete stejné uživatelské prostředí jako v předchozím kurzu – ve vývojovém prostředí se bude dál zobrazovat žlutá obrazovka smrti s podrobnostmi o výjimce, zatímco v produkčním prostředí uvidíte vlastní chybovou stránku. Kromě tohoto existujícího chování je vývojáři odeslán e-mail.

Obrázek 2 znázorňuje e-mail přijatý při návštěvě Genre.aspx?ID=foo. Text e-mailu shrnuje informace o výjimce, zatímco YSOD.htm příloha zobrazuje obsah, který je zobrazený v YSOD podrobností o výjimce (viz obrázek 3).

Snímek obrazovky znázorňující e-mail odeslaný vývojáři

Obrázek 2: Vývojáři je odesláno oznámení Email vždy, když dojde k neošetřené výjimce
(Kliknutím zobrazíte obrázek v plné velikosti.)

Snímek obrazovky, který ukazuje, že e-mailové oznámení obsahuje podrobnosti o výjimce Y S O D jako přílohu.

Obrázek 3: Oznámení Email obsahuje podrobnosti o výjimce YSOD jako přílohu
(Kliknutím zobrazíte obrázek v plné velikosti.)

A co použití vlastní chybové stránky?

Tento kurz ukázal, jak použít Global.asax a obslužnou rutinu Application_Error události ke spuštění kódu, když dojde k neošetřené výjimce. Konkrétně jsme tuto obslužnou rutinu události použili k upozornění vývojáře na chybu. mohli bychom ho rozšířit také o protokolování podrobností o chybách v databázi. Přítomnost obslužné rutiny Application_Error události nemá vliv na prostředí koncového uživatele. Pořád uvidí nakonfigurovanou chybovou stránku, ať už se jedná o YSOD s podrobnostmi o chybě, YSOD chyby za běhu nebo vlastní chybovou stránku.

Při použití vlastní chybové stránky je přirozené se ptát, jestli Global.asax je soubor a Application_Error událost nezbytné. Když dojde k chybě, uživateli se zobrazí vlastní chybová stránka, takže proč nemůžeme vložit kód, který upozorní vývojáře a protokoluje podrobnosti o chybě do třídy kódu na pozadí vlastní chybové stránky? I když určitě můžete přidat kód do třídy kódu na pozadí vlastní chybové stránky, nemáte přístup k podrobnostem o výjimce, která aktivovala Error událost při použití techniky, kterou jsme prozkoumali v předchozím kurzu. GetLastError Volání metody z vlastní chybové stránky vrátí Nothing.

Důvodem tohoto chování je to, že vlastní chybová stránka je přístupná přes přesměrování. Když neošetřená výjimka dosáhne modulu runtime ASP.NET, modul ASP.NET vyvolá svou Error událost (která spustí obslužnou rutinu Application_Error události) a pak přesměruje uživatele na vlastní chybovou Response.Redirect(customErrorPageUrl)stránku tím, že vystaví . Metoda Response.Redirect odešle klientovi odpověď se stavovým kódem HTTP 302 a pokyn prohlížeči, aby si vyžádal novou adresu URL, konkrétně vlastní chybovou stránku. Prohlížeč pak automaticky požádá o tuto novou stránku. Můžete zjistit, že vlastní chybová stránka byla požadována odděleně od stránky, kde chyba pochází, protože panel Adresa prohlížeče se změní na vlastní adresu URL chybové stránky (viz obrázek 4).

Snímek obrazovky, který ukazuje, že se prohlížeč přesměruje, když dojde k chybě

Obrázek 4: Když dojde k chybě, prohlížeč se přesměruje na vlastní adresu URL chybové stránky.
(Kliknutím zobrazíte obrázek v plné velikosti.)

Čistý efekt spočívá v tom, že požadavek, ve kterém došlo k neošetřené výjimce, končí, když server odpoví přesměrováním HTTP 302. Následným požadavkem na vlastní chybovou stránku je zcela nový požadavek. v tomto okamžiku modul ASP.NET zahodil informace o chybě a navíc nemá žádný způsob, jak přidružit neošetřenou výjimku v předchozím požadavku k novému požadavku na vlastní chybovou stránku. To je důvod, proč GetLastError se vrátí null při volání z vlastní chybové stránky.

Je však možné, aby se vlastní chybová stránka spustila během stejného požadavku, který chybu způsobil. Metoda Server.Transfer(url) přenese provádění na zadanou adresu URL a zpracuje ji v rámci stejného požadavku. Kód v obslužné rutině Application_Error události můžete přesunout do třídy kódu na pozadí vlastní chybové stránky a nahradit ho Global.asax následujícím kódem:

protected void Application_Error(object sender, EventArgs e)
{
    // Transfer the user to the appropriate custom error page
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;

    if (lastErrorWrapper.GetHttpCode() == 404)
    {
        Server.Transfer("~/ErrorPages/404.aspx");
    }
    else
    {
        Server.Transfer("~/ErrorPages/Oops.aspx");
    }
}

Když teď dojde k neošetřené výjimce, Application_Error obslužná rutina události přenese řízení na příslušnou vlastní chybovou stránku na základě stavového kódu HTTP. Vzhledem k tomu, že ovládací prvek byl přenesen, má vlastní chybová stránka přístup k neošetřeným informacím o výjimce prostřednictvím Server.GetLastError a může upozornit vývojáře na chybu a protokolovat její podrobnosti. Volání Server.Transfer zastaví modul ASP.NET přesměrování uživatele na vlastní chybovou stránku. Místo toho se jako odpověď na stránku, která chybu vygenerovala, vrátí obsah vlastní chybové stránky.

Souhrn

Pokud ve webové aplikaci ASP.NET dojde k neošetřené výjimce, modul runtime ASP.NET vyvolá Error událost a zobrazí nakonfigurovanou chybovou stránku. Můžeme upozornit vývojáře na chybu, protokolovat její podrobnosti nebo ji zpracovat jiným způsobem vytvořením obslužné rutiny události pro událost Error. Existují dva způsoby, jak vytvořit obslužnou rutinu události pro HttpApplication události, jako Errorje : v Global.asax souboru nebo z modulu HTTP. Tento kurz ukázal, jak v souboru vytvořit obslužnou Error rutinu Global.asax události, která upozorní vývojáře na chybu pomocí e-mailové zprávy.

Vytvoření obslužné Error rutiny události je užitečné, pokud potřebujete zpracovat neošetřené výjimky nějakým jedinečným nebo přizpůsobeným způsobem. Vytvoření vlastní Error obslužné rutiny události pro protokolování výjimky nebo upozornění vývojáře však není nejefektivnějším využitím vašeho času, protože již existují bezplatné a snadno použitelné knihovny protokolování chyb, které lze nastavit během několika minut. V následujících dvou kurzech se podíváme na dvě takové knihovny.

Šťastné programování!

Další čtení

Další informace o tématech probíraných v tomto kurzu najdete v následujících zdrojích informací: