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 ve webové aplikaci 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 v čase. Tento kurz poskytuje přehled o tom, jak ASP.NET zpracovává chyby modulu runtime a sleduje jeden ze způsobů, jak provést vlastní kód při každém neošetřené bublině výjimek až do modulu runtime ASP.NET.
Úvod
Pokud v aplikaci ASP.NET dojde k neošetřené výjimce, bubliny se zobrazí až do modulu runtime ASP.NET, který vyvolá Error
událost a zobrazí příslušnou chybovou stránku. Existují tři různé typy chybových stránek: Běhová chyba Žlutá obrazovka smrti (YSOD); 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 vhodné pro člověka, která odpovídá vzhledu a chování webu, je upřednostňované pro výchozí chybu modulu runtime YSOD, ale zobrazení vlastní chybové stránky je pouze jednou částí komplexního řešení pro zpracování chyb. Když dojde k chybě v aplikaci v produkčním prostředí, je důležité, aby vývojáři byli upozorněni na chybu, aby mohli zjistit příčinu výjimky a vyřešit ji. Kromě toho by se měly zaprotokolovat podrobnosti o chybě, aby bylo možné chybu prozkoumat a diagnostikovat později v čase.
V tomto kurzu se dozvíte, jak získat přístup k podrobnostem neošetřené výjimky, aby bylo možné je protokolovat a upozornit vývojáře. Dva kurzy, které následují po tomto kurzu, prozkoumávají knihovny protokolování chyb, které po určité konfiguraci automaticky upozorní vývojáře na chyby za běhu a protokolují podrobnosti.
Poznámka:
Informace v tomto kurzu jsou nejužitečnější, pokud potřebujete zpracovat neošetřené výjimky určitým jedinečným nebo přizpůsobeným způsobem. V případech, kdy potřebujete jenom protokolovat výjimku a upozornit vývojáře, je použití knihovny protokolování chyb způsob, jak přejít. Následující dva kurzy poskytují přehled dvou takových knihoven.
Spuštění kódu přiError
vyvolání události
Události poskytují objektu mechanismus pro signalizaci, že došlo k něčemu zajímavému, a pro další objekt spustit kód v reakci. Jako ASP.NET vývojář 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 událost tohoto tlačítka Click
a vložíte do ní svůj kód. Vzhledem k tomu, že modul runtime ASP.NET vyvolá událost Error
vždy, když dojde k neošetřené výjimce, následuje kód pro protokolování podrobností o chybě v obslužné rutině události. Jak ale vytvoříte 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 v kanálu HTTP během životnosti požadavku. Například HttpApplication
událost třídyBeginRequest
je vyvolána na začátku každého požadavku; jejíAuthenticateRequest
událost je vyvolána, když modul zabezpečení identifikoval žadatele. Tyto HttpApplication
události dávají vývojáři stránky způsob, jak 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
. Chcete-li vytvořit tento soubor na webu, přidejte novou položku do kořenového adresáře webu pomocí šablony globální třídy aplikace s názvem Global.asax
.
Obrázek 1: Přidání Global.asax
do webové aplikace
(Kliknutím zobrazíte obrázek s plnou velikostí.
Obsah a struktura souboru vytvořeného sadou Visual Studio se mírně liší podle toho, jestli používáte projekt webových Global.asax
aplikací (WAP) nebo projekt webu (WSP). S WAP Global.asax
se implementuje jako dva samostatné soubory - Global.asax
a Global.asax.cs
. Soubor Global.asax
neobsahuje nic jiného než direktivu @Application
, která odkazuje na .cs
soubor; obslužné rutiny událostí, které jsou v souboru definovány Global.asax.cs
. V případě wsPs se vytvoří Global.asax
pouze jeden soubor a obslužné rutiny událostí jsou definovány v <script runat="server">
bloku.
Soubor Global.asax
vytvořený v WAP šablonou globální třídy aplikace sady Visual Studio obsahuje obslužné rutiny událostí s názvem Application_BeginRequest
, Application_AuthenticateRequest
a Application_Error
, které jsou obslužnými rutinami událostí pro HttpApplication
události BeginRequest
, AuthenticateRequest
a Error
v uvedeném pořadí. Existují také obslužné rutiny událostí s názvem Application_Start
, Session_Start
, Application_End
a Session_End
, které jsou obslužné rutiny událostí, které se aktivují při spuštění webové aplikace, při spuštění nové relace, kdy aplikace skončí a kdy relace skončí. Soubor Global.asax
vytvořený ve WSP pomocí sady Visual Studio obsahuje pouze obslužné rutiny Application_Error
událostí , Application_Start
, Session_Start
, Application_End
a Session_End
obslužné rutiny událostí.
Poznámka:
Při nasazování ASP.NET aplikace potřebujete zkopírovat Global.asax
soubor 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í třídy aplikace 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_EventName
události . Do souboru můžete například přidat následující kód Global.asax
pro vytvoření obslužné rutiny události pro danou AuthorizeRequest
událost:
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í třídy aplikace, které nejsou potřeba. Pro účely tohoto kurzu vyžadujeme pro událost pouze obslužnou rutinu Error
události. Ostatní obslužné rutiny událostí ze Global.asax
souboru odeberte.
Poznámka:
Moduly HTTP nabízejí jiný 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. Protože je lze oddělit do knihovny tříd, moduly HTTP nabízejí flexibilnější a opakovaně použitelný model pro vytváření HttpApplication
obslužných rutin událostí. Global.asax
Vzhledem k tomu, že soubor je specifický pro webovou aplikaci, ve které se nachází, lze moduly HTTP zkompilovat do sestavení, v tomto okamžiku je přidání modulu HTTP na web stejně jednoduché jako vyřazení sestavení ve Bin
složce a registraci modulu v Web.config
. Tento kurz se nedívá na vytváření a používání modulů HTTP, ale dvě knihovny protokolování chyb použité v následujících dvou kurzech se implementují 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 komponent ASP.NET.
Načítání informací o neošetřené výjimce
V tuto chvíli máme soubor Global.asax s obslužnou rutinou Application_Error
události. Když se tato obslužná rutina události spustí, 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 výjimce, která byla vyvolána. K načtení podrobností o neošetřené výjimce, která způsobila vyvolání události, použijte metodu objektu Error
GetLastError
serveru.
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. V kódu výše však přetypuji objekt Exception vrácený GetLastError
do objektu HttpException
. Error
Pokud se událost aktivuje, protože během zpracování prostředku ASP.NET byla vyvolána výjimka, je vyvolána výjimka, která byla vyvolána v rámci objektu HttpException
. Chcete-li získat skutečnou výjimku, která precipitovala 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 kvůli požadavku na neexistující stránku, vyvolá se událost, HttpException
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ží proměnnou HttpException
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
se jedná o skutečnou výjimku, 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, které potřebujete k napsání kódu, který zapíše podrobnosti o výjimce do databázové tabulky. Můžete vytvořit tabulku databáze se sloupci pro každou z podrobností o chybě, které vás zajímají – typ, zpráva, trasování zásobníku a tak dále – spolu s dalšími užitečnými informacemi, jako je adresa URL požadované stránky a název 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žte záznam do tabulky. Podobně můžete přidat kód, který upozorní vývojáře na chybu e-mailem.
Knihovny protokolování chyb prověřované v následujících dvou kurzech poskytují tyto funkce, takže toto protokolování chyb a oznámení nemusíte vytvářet sami. Abychom však ukázali, že Error
událost se vyvolává a že obslužnou rutinu Application_Error
události lze použít k protokolování podrobností o chybě a upozornit vývojáře, přidáme kód, který upozorní vývojáře, když dojde k chybě.
Upozornění vývojáře, když dojde k neošetřené výjimce
Když dojde k neošetřené výjimce v produkčním prostředí, je důležité upozornit vývojový tým, aby mohl posoudit chybu a určit, jaké akce je potřeba provést. Pokud se například při připojování k databázi zobrazí chyba, budete muset pečlivě zkontrolovat připojovací řetězec a možná otevřít lístek podpory u vaší společnosti pro hostování webů. Pokud k výjimce došlo kvůli programovací chybě, může být potřeba přidat další logiku kódu nebo ověření, aby se zabránilo těmto chybám v budoucnu.
Třídy rozhraní .NET Framework v System.Net.Mail
oboru názvů usnadňují odesílání e-mailů. Třída MailMessage
představuje e-mailovou zprávu a má vlastnosti, jako To
je , From
, Subject
, Body
a Attachments
. Slouží SmtpClass
k odeslání objektu pomocí zadaného MailMessage
serveru SMTP; nastavení serveru SMTP lze zadat programově nebo deklarativní v <system.net>
prvku v objektu Web.config file
. Další informace o odesílání e-mailových zpráv v aplikaci ASP.NET najdete v tomto článku, odesílání e-mailů z webu ASP.NET webových stránek a System.Net.Mail.
Poznámka:
Element <system.net>
obsahuje nastavení serveru SMTP, které SmtpClient
třída používá při odesílání e-mailu. Vaše společnost pro hostování webů 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.
Do obslužné rutiny události přidejte následující kód Application_Error
, 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ž výše uvedený kód je poměrně dlouhý, většina z něj vytvoří kód HTML, který se zobrazí v e-mailu odeslaném vývojáři. Kód začíná odkazem na HttpException
vrácenou metodou GetLastError
(lastErrorWrapper
). Skutečná výjimka vyvolaná požadavkem se načte prostřednictvím lastErrorWrapper.InnerException
proměnné a je přiřazena lastError
proměnné . Informace o trasování typu, zprávy a zásobníku se načtou ze lastError
tří řetězcových proměnných a uloží se do tří řetězcových proměnných.
Dále se MailMessage
vytvoří objekt s názvem mm
. Text e-mailu je formátovaný html a zobrazuje adresu URL požadované stránky, název aktuálně přihlášeného uživatele a informace o výjimce (typ, zpráva a trasování zásobníku). Jednou z skvělých věcí o HttpException
třídě je, že můžete vygenerovat HTML použitý k vytvoření Exception Details Yellow Screen of Death (YSOD) voláním GetHtmlErrorMessage metoda. Tato metoda se zde používá k načtení značek Podrobnosti výjimky YSOD a jeho 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
vrátí metoda null
.
Posledním krokem je odeslání MailMessage
souboru . 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 a FromAddress
konstantách support@example.com na libovolnou e-mailovou adresu, do které se má e-mail s oznámením o chybě odesílat a pocházet z této adresy. Budete také muset zadat nastavení serveru SMTP v oddílu <system.net>
v Web.config
části . Pokud chcete zjistit nastavení serveru SMTP, které se má použít, obraťte se na poskytovatele webového hostitele.
S tímto kódem, kdykoli dojde k chybě, vývojář odeš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 navštívíte Genre.aspx a předáte neplatnou ID
hodnotu prostřednictvím řetězce dotazu, například Genre.aspx?ID=foo
. Při návštěvě stránky se souborem Global.asax
na místě vznikne stejné uživatelské prostředí jako v předchozím kurzu – ve vývojovém prostředí se bude dál zobrazovat žlutá obrazovka s podrobnostmi o výjimce smrti, zatímco v produkčním prostředí uvidíte vlastní chybovou stránku. Kromě tohoto existujícího chování se vývojář odešle e-mail.
Obrázek 2 ukazuje přijaté e-maily 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 zobrazený v YSOD podrobností o výjimce (viz obrázek 3).
Obrázek 2: Vývojář odešle e-mailové oznámení vždy, když dojde k neošetřené výjimce
(Kliknutím zobrazíte obrázek s plnou velikostí.
Obrázek 3: E-mailové oznámení obsahuje podrobnosti o výjimce YSOD jako přílohu
(Kliknutím zobrazíte obrázek s plnou velikostí.
Co když používáte vlastní chybovou stránku?
V tomto kurzu jsme ukázali, jak použít Global.asax
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, aby protokolovat také podrobnosti o chybách v databázi. Přítomnost obslužné Application_Error
rutiny události nemá vliv na prostředí koncového uživatele. Stále uvidí nakonfigurovanou chybovou stránku, ať už jde o podrobnosti o chybě YSOD, chybu modulu runtime YSOD 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ě, zobrazí se uživateli vlastní chybová stránka, takže proč kód nemůžeme upozornit vývojáře a protokolovat podrobnosti o chybě do třídy kódu na vlastní chybové stránce? I když můžete do třídy kódu za kódem vlastní chybové stránky přidat kód, 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 dosažena přes přesměrování. Když neošetřená výjimka dosáhne ASP.NET modulu runtime ASP.NET vyvolá událost Error
(která spustí obslužnou rutinu Application_Error
události) a pak přesměruje uživatele na vlastní chybovou stránku vydáním Response.Redirect(customErrorPageUrl)
. Metoda Response.Redirect
odešle klientovi odpověď se stavovým kódem HTTP 302, který prohlížeči dává pokyn, aby požádal o novou adresu URL, konkrétně vlastní chybovou stránku. Prohlížeč pak tuto novou stránku automaticky požádá. 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 adresu URL vlastní chybové stránky (viz obrázek 4).
Obrázek 4: Když dojde k chybě, prohlížeč se přesměruje na adresu URL vlastní chybové stránky
(Kliknutím zobrazíte obrázek s plnou velikostí.
Čistým účinkem je, že požadavek, ve kterém došlo k neošetřené výjimce, skončí, když server odpoví přesměrováním HTTP 302. Další požadavek na vlastní chybovou stránku je úplně nový požadavek; tímto bodem 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 zavolá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ý způsobil chybu. Metoda Server.Transfer(url)
přenese provádění na zadanou adresu URL a zpracuje ji ve stejném požadavku. Kód v Application_Error
obslužné rutině události můžete přesunout do třídy kódu 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ž 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 byl ovládací prvek přenesen, má vlastní chybová stránka přístup k neošetřeným informacím o výjimce a Server.GetLastError
může informovat vývojáře o chybě a protokolovat její podrobnosti. Volání Server.Transfer
zastaví ASP.NET modul přesměrování uživatele na vlastní chybovou stránku. Místo toho se obsah vlastní chybové stránky vrátí jako odpověď na stránku, která chybu vygenerovala.
Shrnutí
Když ve webové aplikaci ASP.NET dojde k neošetřené výjimce, vyvolá ASP.NET runtime Error
událost a zobrazí nakonfigurovanou chybovou stránku. Můžeme informovat vývojáře o chybě, protokolovat jeho podrobnosti nebo ho 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 Error
jsou : v Global.asax
souboru nebo z modulu HTTP. Tento kurz ukázal, jak v souboru vytvořit obslužnou rutinu Error
Global.asax
události, která vývojářům oznámí chybu pomocí e-mailové zprávy.
Error
Vytvoření obslužné rutiny události je užitečné, pokud potřebujete zpracovat neošetřené výjimky určitý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 není nejúčinnějším využitím vašeho času, protože už existují bezplatné a snadno použitelné knihovny protokolování chyb, které je možné 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í:
- přehled modulů HTTP a obslužných rutin HTTP ASP.NET
- Řádné reakce na neošetřené výjimky – zpracování neošetřených výjimek
HttpApplication
Třída a objekt aplikace ASP.NET- Obslužné rutiny HTTP a moduly HTTP v ASP.NET
- Odesílání e-mailů v ASP.NET
Global.asax
Principy souboru- Vytváření připojitelných komponent ASP.NET pomocí modulů a obslužných rutin HTTP
- Práce se souborem ASP.NET
Global.asax
- Práce s instancemi
HttpApplication