Condividi tramite


Elaborazione delle eccezioni non gestite (C#)

di Scott Mitchell

Visualizzare o scaricare il codice di esempio (procedura per il download)

Quando si verifica un errore di runtime in un'applicazione Web nell'ambiente di produzione, è importante notificare a uno sviluppatore e registrare l'errore in modo che possa essere diagnosticato in un secondo momento. Questa esercitazione offre una panoramica del modo in cui ASP.NET elabora gli errori di runtime ed esamina un modo per eseguire codice personalizzato ogni volta che un'eccezione non gestita si verifica fino al runtime di ASP.NET.

Introduzione

Quando si verifica un'eccezione non gestita in un'applicazione ASP.NET, viene visualizzata una bolla fino al runtime di ASP.NET, che genera l'evento Error e visualizza la pagina di errore appropriata. Esistono tre diversi tipi di pagine di errore: l'errore di runtime Schermata gialla di morte (YSOD); i dettagli dell'eccezione YSOD; e pagine di errore personalizzate. Nell'esercitazione precedente è stata configurata l'applicazione in modo da usare una pagina di errore personalizzata per gli utenti remoti e i dettagli dell'eccezione YSOD per gli utenti che visitano localmente.

L'uso di una pagina di errore personalizzata descrittiva che corrisponde all'aspetto del sito è preferibile all'impostazione predefinita Errore di runtime YSOD, ma la visualizzazione di una pagina di errore personalizzata è solo una parte di una soluzione completa di gestione degli errori. Quando si verifica un errore in un'applicazione nell'ambiente di produzione, è importante che gli sviluppatori ricevono una notifica dell'errore in modo che possano scoprire la causa dell'eccezione e risolverla. Inoltre, i dettagli dell'errore devono essere registrati in modo che l'errore possa essere esaminato e diagnosticato in un secondo momento.

Questa esercitazione illustra come accedere ai dettagli di un'eccezione non gestita in modo che possano essere registrati e notificati dallo sviluppatore. Le due esercitazioni successive a questa esplorano le librerie di registrazione degli errori che, dopo un po' di configurazione, in modo da notificare automaticamente agli sviluppatori errori di runtime e registrare i relativi dettagli.

Nota

Le informazioni esaminate in questa esercitazione sono più utili se è necessario elaborare eccezioni non gestite in modo univoco o personalizzato. Nei casi in cui è sufficiente registrare l'eccezione e inviare una notifica a uno sviluppatore, l'uso di una libreria di registrazione degli errori è il modo per procedere. Le due esercitazioni successive offrono una panoramica di due librerie di questo tipo.

Esecuzione di codice quando viene generato l'eventoError

Gli eventi forniscono un meccanismo per segnalare che si è verificato qualcosa di interessante e per consentire a un altro oggetto di eseguire il codice in risposta. Gli sviluppatori ASP.NET sono abituati a pensare in termini di eventi. Se si vuole eseguire codice quando il visitatore fa clic su un determinato pulsante, si crea un gestore eventi per l'evento di Click Button e si inserisce il codice. Dato che il runtime ASP.NET genera l'evento Error ogni volta che si verifica un'eccezione non gestita, è necessario che il codice per la registrazione dei dettagli dell'errore venga inserito in un gestore eventi. Ma come si crea un gestore eventi per l'evento Error ?

L'evento Error è uno di molti eventi nella HttpApplication classe che vengono generati in determinate fasi della pipeline HTTP durante la durata di una richiesta. Ad esempio, l'evento HttpApplication della classe viene generato all'inizio di ogni richiesta. Il relativo AuthenticateRequest evento viene generato quando un modulo di BeginRequest sicurezza ha identificato il richiedente. Questi HttpApplication eventi consentono allo sviluppatore di pagine di eseguire la logica personalizzata nei vari punti della durata di una richiesta.

I gestori eventi per gli HttpApplication eventi possono essere inseriti in un file speciale denominato Global.asax. Per creare questo file nel sito Web, aggiungere un nuovo elemento alla radice del sito Web usando il modello Global Application Class con il nome Global.asax.

Sceenshot che evidenzia il file Global dot A A X.

Figura 1: Aggiungere Global.asax all'applicazione Web
(Fare clic per visualizzare l'immagine a dimensione intera)

Il contenuto e la struttura del Global.asax file creato da Visual Studio differiscono leggermente in base al fatto che si usi un progetto applicazione Web (WAP) o un progetto di sito Web (WSP). Con un WAP, Global.asax viene implementato come due file separati, Global.asax e Global.asax.cs. Il file non contiene altro che una @Application direttiva che fa riferimento al .cs file. I Global.asax gestori eventi di interesse vengono definiti nel Global.asax.cs file. Per i provider di servizi di configurazione, viene creato solo un singolo file e Global.asaxi gestori eventi vengono definiti in un <script runat="server"> blocco.

Il Global.asax file creato in un WAP dal modello Classe di applicazione globale di Visual Studio include gestori eventi denominati Application_BeginRequest, Application_AuthenticateRequeste Application_Error, che sono gestori eventi rispettivamente per gli HttpApplication eventi BeginRequest, AuthenticateRequeste Error. Sono inoltre disponibili gestori eventi denominati Application_Start, Session_Start, Application_Ende Session_End, che sono gestori eventi che vengono attivati all'avvio dell'applicazione Web, all'avvio di una nuova sessione, al termine dell'applicazione e alla fine di una sessione, rispettivamente. Il Global.asax file creato in un WSP da Visual Studio contiene solo i Application_Errorgestori eventi , , Application_EndApplication_StartSession_Start, e Session_End .

Nota

Quando si distribuisce l'applicazione ASP.NET è necessario copiare il Global.asax file nell'ambiente di produzione. Il Global.asax.cs file, creato nel WAP, non deve essere copiato nell'ambiente di produzione perché questo codice viene compilato nell'assembly del progetto.

I gestori eventi creati dal modello Global Application Class di Visual Studio non sono esaustivi. È possibile aggiungere un gestore eventi per qualsiasi HttpApplication evento assegnando un nome al gestore eventi Application_EventName. Ad esempio, è possibile aggiungere il codice seguente al Global.asax file per creare un gestore eventi per l'eventoAuthorizeRequest:

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

Analogamente, è possibile rimuovere tutti i gestori eventi creati dal modello classe applicazione globale non necessari. Per questa esercitazione è necessario solo un gestore eventi per l'evento Error . Rimuovere gli altri gestori eventi dal Global.asax file.

Nota

I moduli HTTP offrono un altro modo per definire i gestori eventi per HttpApplication gli eventi. I moduli HTTP vengono creati come file di classe che possono essere inseriti direttamente all'interno del progetto dell'applicazione Web o separati in una libreria di classi separata. Poiché possono essere separati in una libreria di classi, i moduli HTTP offrono un modello più flessibile e riutilizzabile per la creazione HttpApplication di gestori eventi. Mentre il Global.asax file è specifico dell'applicazione Web in cui si trova, i moduli HTTP possono essere compilati in assembly, a questo punto l'aggiunta del modulo HTTP a un sito Web è semplice come eliminare l'assembly nella Bin cartella e registrare il modulo in Web.config. Questa esercitazione non esamina la creazione e l'uso di moduli HTTP, ma le due librerie di registrazione degli errori usate nelle due esercitazioni seguenti vengono implementate come moduli HTTP. Per altre informazioni sui vantaggi dei moduli HTTP, vedere Uso di moduli HTTP e gestori per creare componenti pluggable ASP.NET.

Recupero di informazioni sull'eccezione non gestita

A questo punto è disponibile un file Global.asax con un Application_Error gestore eventi. Quando questo gestore eventi viene eseguito, è necessario inviare una notifica a uno sviluppatore dell'errore e registrarne i dettagli. Per eseguire queste attività, è prima necessario determinare i dettagli dell'eccezione generata. Utilizzare il metodo dell'oggetto GetLastError Server per recuperare i dettagli dell'eccezione non gestita che ha causato la generazione dell'eventoError.

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

Il GetLastError metodo restituisce un oggetto di tipo Exception, che è il tipo di base per tutte le eccezioni in .NET Framework. Tuttavia, nel codice precedente si esegue il cast dell'oggetto Exception restituito da GetLastError in un HttpException oggetto . Se l'evento Error viene generato perché è stata generata un'eccezione durante l'elaborazione di una risorsa ASP.NET, l'eccezione generata viene sottoposta a wrapping all'interno di un oggetto HttpException. Per ottenere l'eccezione effettiva che ha precipitato l'evento Error, utilizzare la InnerException proprietà . Se l'evento Error è stato generato a causa di un'eccezione basata su HTTP, ad esempio una richiesta per una pagina inesistente, viene generata un'eccezione HttpException , ma non ha un'eccezione interna.

Il codice seguente usa per GetLastErrormessage recuperare informazioni sull'eccezione che ha attivato l'evento Error , archiviando in HttpException una variabile denominata lastErrorWrapper. Archivia quindi il tipo, il messaggio e l'analisi dello stack dell'eccezione di origine in tre variabili stringa, verificando se lastErrorWrapper è l'eccezione effettiva che ha attivato l'evento Error (nel caso di eccezioni basate su HTTP) o se si tratta semplicemente di un wrapper per un'eccezione generata durante l'elaborazione della richiesta.

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;
}

A questo punto sono disponibili tutte le informazioni necessarie per scrivere codice che scriva i dettagli dell'eccezione in una tabella di database. È possibile creare una tabella di database con colonne per ognuno dei dettagli dell'errore di interesse, ovvero il tipo, il messaggio, l'analisi dello stack e così via, insieme ad altre informazioni utili, ad esempio l'URL della pagina richiesta e il nome dell'utente attualmente connesso. Application_Error Nel gestore eventi ci si connetterà quindi al database e si inserisce un record nella tabella. Analogamente, è possibile aggiungere codice per avvisare uno sviluppatore dell'errore tramite posta elettronica.

Le librerie di registrazione degli errori esaminate nelle due esercitazioni successive forniscono tali funzionalità predefinite, quindi non è necessario compilare questa registrazione e notifica degli errori manualmente. Tuttavia, per illustrare che l'evento Error viene generato e che il Application_Error gestore eventi può essere usato per registrare i dettagli degli errori e inviare una notifica a uno sviluppatore, aggiungere codice che notifica a uno sviluppatore quando si verifica un errore.

Notifica a uno sviluppatore quando si verifica un'eccezione non gestita

Quando si verifica un'eccezione non gestita nell'ambiente di produzione, è importante avvisare il team di sviluppo in modo che possa valutare l'errore e determinare quali azioni è necessario eseguire. Ad esempio, se si verifica un errore durante la connessione al database, sarà necessario controllare il stringa di connessione e, ad esempio, aprire un ticket di supporto con la società di hosting Web. Se l'eccezione si è verificata a causa di un errore di programmazione, potrebbe essere necessario aggiungere codice aggiuntivo o logica di convalida per evitare tali errori in futuro.

Le classi .NET Framework nello spazio dei nomi semplificano l'invio System.Net.Mail di un messaggio di posta elettronica. La classe rappresenta un messaggio di posta elettronica e ha proprietà come To, SubjectFrom, Body, e Attachments.MailMessage Viene SmtpClass utilizzato per inviare un oggetto utilizzando un MailMessage server SMTP specificato. Le impostazioni del server SMTP possono essere specificate a livello di codice o dichiarativo nell'elemento <system.net> in .Web.config file Per altre informazioni sull'invio di messaggi di posta elettronica in un'applicazione ASP.NET, vedere l'articolo Invio di posta elettronica da un sito Pagine Web ASP.NET e System.Net.Mail.

Nota

L'elemento <system.net> contiene le impostazioni del server SMTP utilizzate dalla classe durante l'invio SmtpClient di un messaggio di posta elettronica. La società di hosting Web ha probabilmente un server SMTP che è possibile usare per inviare messaggi di posta elettronica dall'applicazione. Per informazioni sulle impostazioni del server SMTP da usare nell'applicazione Web, vedere la sezione relativa al supporto dell'host Web.

Aggiungere il codice seguente al Application_Error gestore eventi per inviare uno sviluppatore a un messaggio di posta elettronica quando si verifica un errore:

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);
}

Mentre il codice precedente è piuttosto lungo, la maggior parte di esso crea il codice HTML visualizzato nel messaggio di posta elettronica inviato allo sviluppatore. Il codice inizia facendo riferimento all'oggetto HttpException restituito dal GetLastError metodo (lastErrorWrapper). L'eccezione effettiva generata dalla richiesta viene recuperata tramite lastErrorWrapper.InnerException e viene assegnata alla variabile lastError. Le informazioni di tipo, messaggio e analisi dello stack vengono recuperate da lastError e archiviate in tre variabili stringa.

Viene quindi creato un MailMessage oggetto denominato mm . Il corpo del messaggio di posta elettronica è formattato in formato HTML e visualizza l'URL della pagina richiesta, il nome dell'utente attualmente connesso e le informazioni sull'eccezione (tipo, messaggio e analisi dello stack). Uno degli aspetti interessanti della HttpException classe consiste nel fatto che è possibile generare il codice HTML usato per creare la schermata gialla della morte (YSOD) dei dettagli dell'eccezione chiamando il metodo GetHtmlErrorMessage. Questo metodo viene usato qui per recuperare il markup YSOD dettagli eccezione e aggiungerlo al messaggio di posta elettronica come allegato. Una parola di attenzione: se l'eccezione che ha attivato l'evento Error è stata un'eccezione basata su HTTP (ad esempio una richiesta di una pagina inesistente), il GetHtmlErrorMessage metodo restituirà null.

Il passaggio finale consiste nell'inviare .MailMessage Questa operazione viene eseguita creando un nuovo SmtpClient metodo e chiamando il relativo Send metodo.

Nota

Prima di usare questo codice nell'applicazione Web, è necessario modificare i valori nelle ToAddress costanti e FromAddress da support@example.com a qualsiasi indirizzo di posta elettronica da cui deve essere inviato e originato il messaggio di posta elettronica di notifica degli errori. Sarà anche necessario specificare le impostazioni del server SMTP nella <system.net> sezione in Web.config. Consultare il provider host Web per determinare le impostazioni del server SMTP da usare.

Con questo codice sul posto ogni volta che si verifica un errore, lo sviluppatore invia un messaggio di posta elettronica che riepiloga l'errore e include YSOD. Nell'esercitazione precedente è stato illustrato un errore di runtime visitando Genre.aspx e passando un valore non valido ID tramite la stringa di query, ad esempio Genre.aspx?ID=foo. Visitando la pagina con il Global.asax file sul posto si produce la stessa esperienza utente dell'esercitazione precedente: nell'ambiente di sviluppo si continuerà a visualizzare la schermata gialla della morte dei dettagli dell'eccezione, mentre nell'ambiente di produzione verrà visualizzata la pagina di errore personalizzata. Oltre a questo comportamento esistente, lo sviluppatore invia un messaggio di posta elettronica.

La figura 2 mostra il messaggio di posta elettronica ricevuto quando si visita Genre.aspx?ID=foo. Il corpo del messaggio di posta elettronica riepiloga le informazioni sull'eccezione, mentre l'allegato YSOD.htm visualizza il contenuto visualizzato in Dettagli eccezione YSOD (vedere la figura 3).

Screenshot che mostra il messaggio di posta elettronica inviato allo sviluppatore.

Figura 2: Lo sviluppatore invia una notifica tramite posta elettronica ogni volta che si verifica un'eccezione non gestita
(Fare clic per visualizzare l'immagine a dimensione intera)

Screenshot che mostra che la notifica tramite posta elettronica include i dettagli dell'eccezione Y S O D come allegato.

Figura 3: La notifica tramite posta elettronica include i dettagli dell'eccezione YSOD come allegato
(Fare clic per visualizzare l'immagine a dimensione intera)

Informazioni sull'uso della pagina di errore personalizzata?

Questa esercitazione ha illustrato come usare Global.asax e il Application_Error gestore eventi per eseguire il codice quando si verifica un'eccezione non gestita. In particolare, questo gestore eventi è stato usato per notificare a uno sviluppatore un errore; È possibile estenderlo anche per registrare i dettagli dell'errore in un database. La presenza del Application_Error gestore eventi non influisce sull'esperienza dell'utente finale. Viene comunque visualizzata la pagina di errore configurata, ad esempio dettagli errore YSOD, errore di runtime YSOD o pagina di errore personalizzata.

È naturale chiedersi se il file e Application_Error l'evento Global.asax sono necessari quando si usa una pagina di errore personalizzata. Quando si verifica un errore, l'utente visualizza la pagina di errore personalizzata, quindi perché non è possibile inserire il codice per notificare allo sviluppatore e registrare i dettagli dell'errore nella classe code-behind della pagina di errore personalizzata? Anche se è certamente possibile aggiungere codice alla classe code-behind della pagina di errore personalizzata, non si ha accesso ai dettagli dell'eccezione che ha attivato l'evento Error quando si usa la tecnica esaminata nell'esercitazione precedente. La chiamata al GetLastError metodo dalla pagina di errore personalizzata restituisce Nothing.

Il motivo di questo comportamento è dovuto al raggiungimento della pagina di errore personalizzata tramite un reindirizzamento. Quando un'eccezione non gestita raggiunge il runtime di ASP.NET il motore di ASP.NET genera il relativo Error evento (che esegue il gestore eventi) e quindi reindirizza l'utente Application_Error alla pagina di errore personalizzata eseguendo un oggetto Response.Redirect(customErrorPageUrl). Il Response.Redirect metodo invia una risposta al client con un codice di stato HTTP 302, indicando al browser di richiedere un nuovo URL, ovvero la pagina di errore personalizzata. Il browser richiede quindi automaticamente questa nuova pagina. È possibile indicare che la pagina di errore personalizzata è stata richiesta separatamente dalla pagina in cui è stato generato l'errore perché la barra degli indirizzi del browser cambia nell'URL della pagina di errore personalizzata (vedere la figura 4).

Screenshot che mostra che il browser viene reindirizzato quando si verifica un errore.

Figura 4: Quando si verifica un errore, il browser viene reindirizzato all'URL della pagina degli errori personalizzata
(Fare clic per visualizzare l'immagine a dimensione intera)

L'effetto netto è che la richiesta in cui si è verificata l'eccezione non gestita termina quando il server risponde con il reindirizzamento HTTP 302. La richiesta successiva alla pagina di errore personalizzata è una nuova richiesta; da questo punto il motore di ASP.NET ha eliminato le informazioni sull'errore e, inoltre, non ha modo di associare l'eccezione non gestita nella richiesta precedente alla nuova richiesta per la pagina di errore personalizzata. Questo è il motivo per cui GetLastError restituisce null quando viene chiamato dalla pagina di errore personalizzata.

Tuttavia, è possibile che la pagina di errore personalizzata venga eseguita durante la stessa richiesta che ha causato l'errore. Il Server.Transfer(url) metodo trasferisce l'esecuzione all'URL specificato ed elabora l'esecuzione all'interno della stessa richiesta. È possibile spostare il codice nel Application_Error gestore eventi nella classe code-behind della pagina di errore personalizzata, sostituendolo con Global.asax il codice seguente:

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");
    }
}

Ora quando si verifica un'eccezione non gestita, il gestore eventi trasferisce il Application_Error controllo alla pagina di errore personalizzata appropriata in base al codice di stato HTTP. Poiché il controllo è stato trasferito, la pagina di errore personalizzata ha accesso alle informazioni sulle eccezioni non gestite tramite Server.GetLastError e può inviare una notifica a uno sviluppatore dell'errore e registrarne i dettagli. La Server.Transfer chiamata impedisce al motore di ASP.NET di reindirizzare l'utente alla pagina di errore personalizzata. Il contenuto della pagina di errore personalizzata viene invece restituito come risposta alla pagina che ha generato l'errore.

Riepilogo

Quando si verifica un'eccezione non gestita in un'applicazione Web ASP.NET, il runtime di ASP.NET genera l'evento Error e visualizza la pagina di errore configurata. È possibile informare lo sviluppatore dell'errore, registrarne i dettagli o elaborarlo in altro modo creando un gestore eventi per l'evento Error. Esistono due modi per creare un gestore eventi per HttpApplication eventi come Error: nel Global.asax file o da un modulo HTTP. Questa esercitazione ha illustrato come creare un Error gestore eventi nel Global.asax file che informa gli sviluppatori di un errore tramite un messaggio di posta elettronica.

La creazione di un Error gestore eventi è utile se è necessario elaborare eccezioni non gestite in modo univoco o personalizzato. Tuttavia, la creazione di un gestore eventi personalizzato Error per registrare l'eccezione o per notificare a uno sviluppatore non è l'uso più efficiente del tempo perché esiste già disponibile e facile da usare librerie di registrazione degli errori che possono essere configurate in pochi minuti. Le due esercitazioni successive esaminano due librerie di questo tipo.

Buon programmatori!

Altre informazioni

Per altre informazioni sugli argomenti illustrati in questa esercitazione, vedere le risorse seguenti: