Creazione di componenti ASP.NET collegabili mediante moduli e gestori HTTP

Scott Mitchell, 4GuysFromRolla.com
Atif Aziz, Skybow AG

Settembre 2004

Riassunto: In questo articolo, Scott Mitchell e Atif Aziz mostrano come utilizzare i moduli e i gestori HTTP per aggiungere la registrazione degli errori alle proprie applicazioni ASP.NET (22 pagine stampate).

Questo articolo contiene collegamenti a siti e pagine Web in inglese.

Scaricare il file di esempio MSDNElmah.msi.

Sommario

Introduzione
ELMAH: moduli e gestori per la registrazione degli errori
Breve panoramica sui gestori e i moduli HTTP
Analisi dell'architettura di ELMAH
Aggiunta di ELMAH ad un'applicazione Web ASP.NET
Conclusioni
Riferimenti
Pubblicazioni correlate

Introduzione

Avete mai utilizzato un'applicazione ASP.NET e progettato delle caratteristiche e funzionalità desiderando di poterle poi riutilizzare facilmente in un'altra applicazione ASP.NET? ASP.NET offre diversi strumenti per la componentizzazione di diversi tipi di funzionalità. Gli strumenti più comuni per il riutilizzo in ASP.NET sono due:

  • I controlli utente e i controlli server personalizzati e compilati per gli elementi e la funzionalità dell'interfaccia utente.
  • Le librerie di classe .NET per la logica aziendale e il codice di accesso ai dati.

Esistono due strumenti di riutilizzo di ASP.NET non molto utilizzati che sono i moduli e i gestori HTTP.

Se non sapete cosa siano i gestori e i moduli HTTP, non preoccupatevi. Ce ne occuperemo più avanti nell'articolo. Per il momento, basta sapere che i moduli HTTP sono classi che è possibile configurare in modo da essere eseguite in risposta ad eventi generati durante una richiesta per una risorsa ASP.NET. Un gestore HTTP è una classe responsabile di rendere una particolare risorsa o tipo di risorsa. Ogni volta che una pagina Web viene aggiunta al progetto, si sta essenzialmente scrivendo un gestore HTTP. Ciò accade perché quando la parte HTML di una pagina Web ASP.NET viene compilata dinamicamente in fase di esecuzione, direttamente o indirettamente eredita da System.Web.UI.Page, che non è altro che l'implementazione di un gestore HTTP. Ciò si verifica a prescindere dall'eventuale strategia adottata, sia quella inline o quella del codice sottostante.

Come sapete, un'applicazione ASP.NET consiste solitamente di un insieme di pagine Web che vengono richiamate quando richieste dal browser di un utente finale. Come sviluppatori di ASP.NET, la maggior parte del codice che scriviamo è specifico per una richiesta di una particolare pagina Web, come il codice di una classe dal codice sottostante di una particolare pagina per visualizzare i risultati del database basati su una qualche query di ricerca. A volte, però, occorre scrivere del codice che sia ortogonale ad una singola pagina Web, codice che si applica a tutte le pagine in un'applicazione. Supponiamo, ad esempio, di voler registrare l'ordine in cui ciascun utente si sposta sul sito Web. Ciascuna pagina dovrà registrare il momento della richiesta e le informazioni che identificano l'utente.

Un modo per realizzare questa funzionalità di registrazione consiste nell'aggiungere il codice che ha registrato i dati pertinenti in un database nel gestore eventi Page_Load per ciascuna pagina Web del sito. Un approccio di questo tipo risulta, tuttavia, difficile da gestire o riutilizzare. Ogni volta che aggiungiamo una nuova pagina ASP.NET al sito, dobbiamo assicurarci di includere il codice di registrazione appropriato. Per aggiungere della funzionalità simile ad un altro sito, dovremmo aggiungere il codice necessario in ciascuna pagina di quel sito. Idealmente, la funzionalità di registrazione sarebbe logicamente e fisicamente separata dalla funzionalità di ciascuna singola pagina e aggiungerla ad un altro sito dovrebbe essere semplice come trascinare un assembly nella directory /bin del sito.

Con i moduli e i gestori HTTP, tale riutilizzo e tale gestione sono possibili. Nell'articolo esamineremo un insieme di moduli e gestori HTTP che sono stati progettati per rendere la registrazione degli errori un'operazione altamente gestibile e riutilizzabile. L'obiettivo dell'articolo è quello di dimostrare come utilizzare i gestori e i moduli HTTP come tipo di componentizzazione ad alto livello, consentendo lo sviluppo, l'impacchettamento e la distribuzione di interi insiemi di funzionalità come un'unica unità e indipendente dalle applicazioni Web. Per raggiungere l'obiettivo esamineremo un'applicazione che beneficia del riutilizzo e componentizzazione mediante i gestori e i moduli HTTP.

ELMAH: moduli e gestori per la registrazione degli errori

I moduli e i gestori per la registrazione degli errori (ELMAH: Error Logging Modules And Handlers) che esamineremo nel corso dell'articolo, sono stati scritti dal coautore Atif Aziz (www.raboof.com/) e rappresentano dei semplici mezzi per aggiungere funzionalità di registrazione degli errori ad un'applicazione Web ASP.NET. ELMAH mostra come utilizzare i moduli e i gestori HTTP in modo da fornire un altro grado di componentizzazione per il codice ortogonale all'applicazione Web (come la registrazione a livello dell'applicazione). ELMAH rappresenta una vera soluzione collegabile, dal momento che è possibile aggiungerla dinamicamente ad un'applicazione Web ASP.NET in esecuzione senza dover rieseguire la compilazione o la distribuzione.

Anche quando un'applicazione Web è ben scritta e testata, è possibile che qualcosa vada storto. Potrebbe non trattarsi di un problema del codice, ma potrebbe essere il server di posta elettronica che non risponde o un danneggiamento dei dati che causa un errore di crittografia. Indipendentemente dal motivo, quando si verifica un errore, specialmente su un sito live, è fondamentale registrare i dettagli dell'errore per poter diagnosticare il problema. ELMAH fornisce un meccanismo centralizzato per la registrazione e la notifica degli errori. Quando in un'applicazione ASP.NET si verifica un'eccezione che non viene intercettata, ELMAH riceve la notifica e gestisce l'eccezione come descritto nel file Web.config. Ciò può significare registrare i dettagli dell'errore in un database, inviare un messaggio di posta elettronica all'amministratore o entrambe le cose.

ELMAH non è progettata per rispondere normalmente ad eccezioni non gestite. Semplicemente registra i dettagli delle eccezioni non gestite. Quando si aggiunge ELMAH ad un'applicazione Web ASP.NET, qualunque eccezione non gestita generata nell'applicazione viene registrata. Quando si verifica un'eccezione non gestita, ELMAH non influisce sull'interazione da parte dell'utente finale. Gli utenti vedranno sempre la pagina "Errore del server" o, se vi sono errori personalizzati configurati per la gestione di errori HTTP 500, verranno reindirizzati su una pagina con un messaggio più significativo. Dietro le quinte, tuttavia, ELMAH avrà determinato che si è verificata un'eccezione e ne avrà registrato i dettagli.

ELMAH scopre le eccezioni non gestite mediante l'evento Error dell'oggetto HttpApplication. L'evento Error viene generato quando un'eccezione non rivelata si propaga durante l'elaborazione di una richiesta che può venire da una libreria di classe .NET o da una pagina Web ASP.NET. Tenere presente che molte applicazioni ASP.NET implementano in modo non corretto la gestione di pagine di errori personalizzate mediante il metodo Server.ClearError(). La cancellazione dell'errore impedisce all'evento Error di essere generato (così come di essere riportato al client) ed ELMAH non avrà mai la possibilità di registrare l'eccezione. In altre parole, utilizzando ClearError() in una pagina di errore personalizzata, l'utente si accorgerà che si è verificato un errore, ma voi no.

Nota   Per ulteriori informazioni sulla creazione di pagine di errore personalizzate, leggere l'articolo Gestione degli errori personalizzata tramite ASP.NET di Eli Robillard.
Nota   Quando si verifica un'eccezione non gestita in un servizio Web ASP.NET, l'evento Error non viene propagato ai moduli HTTP e quindi a ELMAH. Viene piuttosto intercettato dal runtime ASP.NET e al client viene restituito un errore SOAP. Per far sì che un errore venga registrato in un servizio Web, occorre creare un'estensione SOAP che rilevi gli errori SOAP.

Oltre a registrare i dettagli di eccezioni non gestite, ELMAH è fornita di un insieme di gestori HTTP per la visualizzazione del registro degli errori. Esiste un'interfaccia Web del registro che fornisce un elenco di tutti gli errori non gestiti e i dettagli per un particolare errore (vedere le figure 1 e 2).

Figura 1. Visualizzazione del registro degli errori

Figura 2. Visualizzazione di un errore

Tale registro degli errori viene anche reso come RSS, il che consente ad un amministratore di ricevere notifiche mediante l'aggregatore RSS preferito quando si è verificato un errore (vedere la figura 3).

Figura 3. Versione RSS degli errori

Nota   RSS (Really Simple Syndication) è uno standard in formato XML utilizzato comunemente per fornire notizie o altri tipi di contenuto variabile. Per ulteriori informazioni su RSS, compreso come fornire del contenuto utilizzando RSS e come creare un lettore RSS basato su Web, si consiglia la lettura di Creating an Online News Aggregator with ASP.NET.

Per brevità, questo articolo accenna solo a un sottoinsieme delle funzionalità di ELMAH, concentrandosi sui componenti fondamentali. Dall'articolo è possibile scaricare il codice completo e vi incoraggiamo a studiarlo attentamente per giungere ai dettagli di implementazione. All'indirizzo workspaces.gotdotnet.com/elmah si trova anche un'installazione dell'area di lavoro GotDotNet per ELMAH a fini di discussione, segnalazione di problemi e aggiornamenti.

Soluzioni esistenti per la registrazione centralizzata degli errori

Anche se ASP.NET non fornisce la registrazione degli errori e le funzionalità di visualizzazione incorporate, il gruppo Patterns & Practices di Microsoft ha creato un registratore degli errori open-source: Exception Management Application Block (EMAB). EMAB è stato progettato per funzionare sia con applicazioni .NET desktop che basate su Web, ma si intuisce che EMAB sia stato principalmente progettato per le applicazioni desktop e solo successivamente per le applicazioni Web poiché EMAB, per impostazione predefinita, pubblica i dettagli delle eccezioni sul registro eventi di Windows. Mentre il registro eventi è un archivio di backup adatto alle informazioni concise sulle eccezioni per un'applicazione desktop, la maggior parte delle applicazioni Web, specialmente quelle ospitate su un server condiviso di una società di Web hosting, si tengono lontane dal registro eventi poiché per utilizzarlo è necessario stabilire dei permessi speciali che consentano all'applicazione ASP.NET di scrivere su un registro eventi. EMAB è abbastanza flessibile da creare un editore personalizzato per registrare informazioni su un database, ma ciò comporta un compito ulteriore per gli sviluppatori.

Nota   ELMAH comprende un modulo di accesso al database per Microsoft SQL Server 2000, del quale ci occuperemo più avanti. Con ELMAH è possibile creare registratori di eccezioni personalizzati, come un registratore che registra i dettagli delle eccezioni su un file XML del file system del server Web. Se si possiede un editore personalizzato scritto per EMAB che si desidera utilizzare, è infatti possibile estendere ELMAH per utilizzare EMAB.

La modalità in cui EMAB viene utilizzato per la registrazione delle informazioni sulle eccezioni condiziona notevolmente la gestione e il riutilizzo dell'applicazione Web. Ad esempio, un semplice approccio per registrare informazioni sulle eccezioni consiste nel racchiudere ciascun blocco di codice in ciascuna pagina Web ASP.NET da un blocco try ...catch, chiamando EMAB nella sezione catch.

private void Page_Load(object sender, EventArgs e)
{
  try {
    // Code that might cause an exception
  }
  catch (Exception ex) {
    // record exception information by calling exception logger library
  }
}

Un tale approccio non è consigliabile poiché collega strettamente la registrazione delle eccezioni per ogni pagina Web ASP.NET, rendendola niente affatto gestibile e riutilizzabile. Un approccio migliore consiste nell'utilizzare EMAB nell'evento Application_Error in Global.asax. Questo approccio offre un'architettura non strettamente collegata, più gestibile e riutilizzabile. Il codice di pubblicazione delle eccezioni, infatti, non tocca alcuna pagina Web ASP.NET e si trova in una posizione centralizzata. Lo svantaggio di questo approccio è che non è collegabile. Per aggiungere questa funzionalità di registrazione degli errori ad un'altra applicazione Web ASP.NET, occorre modificare Global.asax di quell'applicazione, necessario per ricompilare e ridistribuire l'applicazione.

Lo scopo dell'articolo non è quello di introdurre un sostituto di EMAB. Piuttosto, è quello di evidenziare la componentizzazione resa possibile dai gestori e dai moduli HTTP. ELMAH mostra come sia possibile prendere un'operazione comune, quale la registrazione degli errori centralizzata, e componentizzarla per facilitarne la gestione e consentire un alto grado di riutilizzo. Lo scopo di ELMAH è quello di offrire assistenza per la componentizzazione di funzionalità applicabile.

Breve panoramica sui gestori e i moduli HTTP

Prima di esaminare le specifiche dell'architettura ed implementazione di ELMAH, occupiamoci un attimo dei gestori e dei moduli HTTP. Ogni volta che arriva una richiesta ad un server Web IIS, IIS esamina l'estensione della richiesta per decidere come procedere. Quando si tratta di contenuto statico come pagine HTML, file CSS, immagini, file JavaScript e così via, IIS gestisce da solo la richiesta. Nel caso di contenuto dinamico come pagine ASP, pagine Web ASP.NET e servizi Web ASP.NET, IIS delega la richiesta ad una estensione ISAPI specificata. Un'estensione ISAPI è un pezzo di codice non gestito in grado di eseguire il rendering di un tipo particolare di richieste. Ad esempio, l'estensione ISAPI asp.dll esegue il rendering di richieste per pagine Web ASP classiche; l'estensione ISAPI aspnet_isapi.dll viene richiamata quando giunge una richiesta per una risorsa ASP.NET.

Oltre alle estensioni ISAPI, IIS include anche i filtri ISAPI. Un filtro ISAPI è un pezzo di codice non gestito che può essere eseguito in risposta ad eventi generati da IIS. Durante il ciclo di vita di una richiesta, IIS passa attraverso un numero di passaggi che generano eventi corrispondenti. Ad esempio, un evento viene generato appena la richiesta raggiunge IIS, quando la richiesta sta per essere autenticata, quando il contenuto reso sta per essere reinviato al client e così via. I filtri ISAPI vengono normalmente utilizzati per fornire funzionalità come la riscrittura URL, la compressione, l'autenticazione e l'autorizzazione specializzate e la registrazione specializzata.

Quando la richiesta per una risorsa ASP.NET raggiunge IIS, viene reindirizzata al modulo di gestione di ASP.NET, che rende il contenuto per la risorsa richiesta. Il modulo di gestione di ASP.NET si comporta come IIS in quanto genera un numero di eventi quando la richiesta passa attraverso la pipeline HTTP di ASP.NET. Il modulo di gestione di ASP.NET delega, inoltre, il rendering della risorsa richiesta ad una particolare classe. Mentre IIS utilizza le estensioni e i filtri ISAPI non gestiti, ASP.NET utilizza le classi gestite chiamate gestori e moduli HTTP.

Un gestore HTTP è una classe responsabile di rendere un particolare tipo di risorsa. Ad esempio, la classe a codice sottostante per una pagina Web ASP.NET è un gestore HTTP in grado di rendere il tag per una particolare pagina Web. Conviene pensare ai gestori come a dei renderer specializzati in grado di creare il tag per un tipo particolare di risorse.

Nota   Per informazioni più approfondite sui gestori HTTP e per applicazioni pratiche dei gestori, si consiglia di leggere Serving Dynamic Content with HTTP Handlers.

Un modulo HTTP è una classe in grado di accedere ai vari eventi generati al passaggio di una richiesta attraverso le fasi della sua durata sul server. Un evento di applicazione ASP.NET di questo tipo è l'evento Error, che viene generato quando si verifica un'eccezione non gestita e che rappresenta l'evento al quale è interessata ELMAH.

Nota   Per ulteriori informazioni sui moduli HTTP, compresa una panoramica su come utilizzare i moduli HTTP per implementare la riscrittura URL, consultare l'articolo Riscrittura URL in ASP.NET.

La figura 4 fornisce una rappresentazione grafica della pipeline HTTP di ASP.NET. Notare che il processo inizia con una richiesta che arriva a IIS. Supponendo che la risorsa richiesta sia configurata per poter essere gestita dall'estensione ISAPI ASP.NET, IIS smista la richiesta all'estensione ISAPI aspnet_isapi.dll non gestita. Questa estensione ISAPI passa la richiesta al modulo di gestione ASP.NET gestito. Durante la durata della richiesta, è possibile che vengano eseguiti uno o più moduli HTTP, a seconda di quali moduli siano stati registrati e quali eventi abbiano sottoscritto. Infine, il modulo di gestione ASP.NET determina il gestore HTTP responsabile di rendere il contenuto, richiamando il gestore e restituendo il contenuto generato a IIS, che lo restituisce al client richiedente.

Figura 4. Flusso di dati attraverso il registratore degli errori

ELMAH fornisce la registrazione centralizzata degli errori mediante un modulo HTTP che possiede un gestore eventi per l'evento Error. Quando viene generato l'evento, ELMAH registra i dettagli per l'eccezione. ELMAH utilizza, inoltre, dei gestori HTTP che sono principalmente responsabili di generare tag HTML e RSS per visualizzare le informazioni del registro degli errori.

La configurazione di un'applicazione Web esistente in grado di utilizzare vari gestori e moduli viene realizzata copiando l'assembly del modulo o del gestore nella directory /bin dell'applicazione Web ed aggiungendo alcune righe di configurazione al file Web.config.

Per configurare i moduli HTTP per un'applicazione Web, includere una sezione <httpModules> al file Web.config che specifica il tipo del modulo da aggiungere:

<httpModules>
   <add name="ModuleName" type="ModuleType" />
</httpModules>

ModuleType è una stringa che indica il tipo di modulo, che consiste nel nome di classe completo (Namespace.ClassName) seguito dal nome dell'assembly. L'attributo type può includere anche informazioni sulla versione e la lingua insieme ad un token di chiave pubblica richiesto per gli assembly dal nome sicuro. Il pezzo di codice seguente mostra l'impostazione effettiva per <httpModules> che occorre utilizzare per includere il modulo di registrazione degli errori di ELMAH nella propria applicazione ASP.NET:

<httpModules>
  <add name="ErrorLog" type="GotDotNet.Elmah.ErrorLogModule, 
    GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
    PublicKeyToken=978d5e1bd64b33e5" />
</httpModules>

Aggiungendo una sezione <httpHandlers> al file Web.config sarà possibile utilizzare un gestore HTTP in un'applicazione Web. Poiché un gestore HTTP rende il contenuto per un particolare tipo di risorse, oltre all'attributo type, l'elemento <httpHandlers> contiene un attributo path, che indica il percorso o estensione di file da associare a questo gestore HTTP. Vi è inoltre un attributo verb che consente di limitare l'utilizzo del gestore a tipi specifici di richieste HTTP, come in una richiesta GET o POST. Nell'esempio seguente viene creato un gestore HTTP che viene richiamato per tutte le richieste a file con estensione .ashx.

<httpHandlers>
   <add verb="*" path="*.ashx" type="HandlerType" />
</ httpHandlers >

L'attributo type per il gestore HTTP viene espresso mediante le stesse opzioni di sintassi dei moduli HTTP. È possibile inserire queste impostazioni di Web.config anche nel file machine.config, che ha l'effetto di abilitare i gestori e i moduli per tutte le applicazioni Web del server. Il pezzo di codice successivo mostra l'elemento <httpHandlers> nel file Web.config nella dimostrazione che è contenuta nel codice scaricabile da questo articolo. Notare che è indicato che ogni richiesta in arrivo a /elmah/default.aspx venga resa dalla classe ErrorLogPageFactory.

<httpHandlers>
    <add 
        verb="POST,GET,HEAD" 
        path="elmah/default.aspx" 
        type="GotDotNet.Elmah.ErrorLogPageFactory, 
          GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
          PublicKeyToken=978d5e1bd64b33e5" />
</httpHandlers>

Abbiamo visto quanto sia semplice aggiungere moduli e gestori HTTP ad un'applicazione Web; ciò può essere eseguito in pochi secondi e non richiede alcuna ricompilazione o ridistribuzione dell'applicazione ASP.NET. Questo fa dei moduli e dei gestori HTTP un ottimo strumento per il riutilizzo, fornendo un mezzo per componentizzare l'applicazione in parti non strettamente collegate e altamente gestibili.

Analisi dell'architettura di ELMAH

L'architettura di ELMAH consiste di tre sottosistemi:

  • Un sottosistema per la registrazione degli errori
  • Un sottosistema del modulo HTTP
  • Un sottosistema del gestore HTTP

Il sottosistema per la registrazione degli errori ha due compiti: quello di registrare gli errori nel registro e quello di recuperare le informazioni sugli errori dal registro. Il sottosistema del modulo HTTP registra un errore quando si verifica un'eccezione non gestita nell'applicazione ASP.NET. Il sottosistema del gestore HTTP fornisce un mezzo per rendere il registro degli errori in tag, costituendo un'interfaccia basata su Web per il registro degli errori e una versione RSS.

Come mostra la figura 5, entrambi i sottosistemi del modulo e del gestore HTTP utilizzano il sottosistema per la registrazione degli errori. Il sottosistema del modulo HTTP invia le informazioni sulle eccezioni al sottosistema per la registrazione degli errori, mentre il sottosistema del gestore HTTP legge e rende le informazioni sull'errore.

Figura 5. Sottosistema per la registrazione degli errori

Per una migliore comprensione dell'architettura di ELMAH, esaminiamo i tre sottosistemi in maniera più dettagliata.

Sottosistema per la registrazione degli errori

Il sottosistema per la registrazione degli errori registra gli errori nel registro e possiede delle funzionalità per il recupero dei dettagli di un particolare errore o di un sottoinsieme di errori. Questa funzionalità è resa disponibile da un certo numero di classi:

  • ErrorLog: Classe astratta che fornisce i metodi contrattuali sia per leggere che per scrivere sul registro.
  • Error: Classe che contiene le proprietà per descrivere i dettagli di un particolare errore.
  • ErrorLogEntry: Classe che rappresenta una particolare istanza Error per un particolare ErrorLog. ErrorLogEntry raggruppa essenzialmente un'istanza Error con l'istanza ErrorLog da cui deriva.

Esaminiamo ora le tre classi e il relativo funzionamento con i sottosistemi dei moduli HTTP e dei gestori HTTP in modo da fornire un'utilità completa per la registrazione centralizzata delle eccezioni.

Analisi della classe ErrorLog

A seconda della particolare strategia o installazione del progetto, verrà utilizzato un diverso archivio di backup per il registro degli errori. Su un server di produzione, ad esempio, le eccezioni saranno registrate su Microsoft SQL Server, mentre su un server di sviluppo sarà sufficiente archiviare gli errori in un insieme di file XML o in un database di Microsoft Access. Per poter utilizzare diversi archivi di backup, il sottosistema per la registrazione degli errori fornisce una classe di base astratta, ErrorLog, che definisce i metodi di base che tutti i registratori di errori ELMAH devono implementare. I metodi sono:

  • Log(Error): Registra un errore nell'archivio di backup. La classe Error rappresenta le informazioni su un'eccezione non gestita; tra breve ci occuperemo della classe Error più dettagliatamente. Nel registrare le informazioni sull'errore, il metodo Log() deve anche assegnare un identificatore univoco all'errore.
  • GetError(id): Restituisce informazioni su un particolare errore nel registro.
  • GetErrors(...): Restituisce un sottoinsieme di errori dal registro. Il metodo viene utilizzato dal sottosistema del gestore HTTP per visualizzare il registro degli errori per pagina, piuttosto che visualizzare tutti gli errori insieme.

ELMAH comprende due implementazioni ErrorLog:

  • SqlErrorLog: Registra gli errori su un database di Microsoft SQL Server 2000 utilizzando il provider System.Data.SqlClient. SqlErrorLog richiede SQL Server 2000 poiché usufruisce di alcune sue funzionalità XML; si tratta però solo di un dettaglio di implementazione soggetto a modifiche.
  • MemoryErrorLog: Registra gli errori nella memoria dell'applicazione (RAM). In altre parole, è associato ad AppDomain tanto che ciascuna applicazione riceve il proprio registro privato. Inutile dire che questo registro non sopravvive al riavvio o alla durata dell'applicazione ed è quindi utile essenzialmente ai fini del testing e di una temporanea risoluzione dei problemi quando altre implementazioni non funzionano.

È possibile utilizzare entrambi i registratori delle eccezioni semplicemente aggiungendo un paio di righe di testo al file Web.config dell'applicazione Web ASP.NET. Se occorre archiviare i dettagli degli errori su un archivio che non sia SQL Server o la memoria dell'applicazione, è possibile creare il proprio registratore personalizzato. Per implementare un registratore degli errori per ELMAH, creare una classe che estenda ErrorLog e fornire l'implementazione per Log(), GetError() e GetErrors() relativi all'archivio desiderato.

Entrambi i sottosistemi del modulo e del gestore HTTP in ELMAH interagiscono direttamente con la classe ErrorLog specificata, sia questa SqlErrorLog, MemoryErrorLog o la propria classe di registro personalizzata. Il modulo HTTP registra le informazioni sulle eccezioni creando un'istanza Error e passandola al metodo Log() del metodo ErrorLog. I gestori HTTP leggono i dettagli di uno o più errori mediante i metodi GetError() e GetErrors() di ErrorLog, che restituiscono un'istanza ErrorLogEntry specifica oppure un insieme di istanze ErrorLogEntry.

Analisi della classe Error

Il metodo Log() di ErrorLog prevede un parametro di input di tipo Error. Invece della classe Exception fornita in .NET Framework, viene utilizzata una classe Error personalizzata poiché la classe Exception è più adatta a comunicare le informazioni sulle eccezioni attraverso lo stack di codice e durante la durata di un'applicazione. Gli oggetti Exception, tuttavia, non sono indicati per l'archiviazione di un registro di eccezioni per problemi di archivio, digitazione e portabilità. Sebbene sia possibile utilizzare la serializzazione binaria per archiviare un'istanza Exception, ciò richiederebbe che l'oggetto Exception sia deserializzabile su un computer con lo stesso insieme di tipi ed assembly disponibili. Ciò costituisce un limite inaccettabile (specialmente dal punto di vista amministrativo e operativo) poiché un registro ed il relativo contenuto devono essere portatili e non tali da poter essere visualizzati solo su un computer dalla configurazione o esecuzione particolari. Inoltre, un'istanza Exception manca spesso di informazioni di periferia specifiche ad un'applicazione Web, come i valori dell'insieme ServerVariables della richiesta Web corrente, che sono invece preziosi ai fini della diagnosi. In breve, la classe Error agisce come un surrogato per tutti i tipi di eccezione, contenendo le informazioni per un'eccezione generata in un'applicazione Web.

Nella tabella 1 sono elencate tutte le proprietà di Error.

Proprietà Descrizione
Exception Istanza Exception rappresentata da questo errore. Si tratta di una proprietà solo della fase di esecuzione che non persiste mai insieme ad un'istanza della classe.
ApplicationName Nome dell'applicazione nella quale si è verificato l'errore.
HostName Nome del computer host nel quale si è verificato l'errore. Una buona scelta per un valore predefinito è dato da Environment.MachineName.
Type Tipo, classe o categoria dell'errore. Di solito, sarebbe il nome di tipo completo (senza la qualificazione dell'assembly) dell'eccezione.
Source Origine dell'errore, solitamente la stessa della proprietà Message di un oggetto Exception.
Message Breve testo che descrive l'errore, di solito lo stesso della proprietà Message di un oggetto Exception.
Detail Testo dettagliato dell'errore, come la traccia dello stack completa.
User Utente che aveva accesso all'applicazione al momento dell'errore, come quello restituito da Thread.CurrentPrincipal.Identity.Name.
Time Data e ora in cui si è verificato l'errore. Viene sempre utilizzata l'ora locale.
StatusCode Codice di stato restituito nell'intestazione di risposta come risultato dell'errore. Ad esempio, 404 corrisponde all'eccezione FileNotFoundException. Sfortunatamente, questo valore non può essere sempre determinato in modo affidabile da ASP.NET. In alcuni casi il valore StatusCode viene riportato come zero.
WebHostHtmlMessage Messaggio HTML predefinito generato dal Web host (ASP.NET) in assenza di pagine di errore personalizzate.
ServerVariables Insieme NameValueCollection di variabili server Web come quelle contenute in HttpRequest.ServerVariables.
QueryString Insieme NameValueCollection di variabili stringhe di query HTTP come quelle contenute in HttpRequest.QueryString.
Form Insieme NameValueCollection di variabili form come quelle contenute in HttpRequest.Form.
Cookies Insieme NameValueCollection di cookie inviati dal client come quelli contenuti in HttpRequest.Cookies.

Occorre dare delle spiegazioni circa la proprietà WebHostHtmlMessage. Se l'applicazione Web ASP.NET incontra un'eccezione non gestita e l'applicazione non è configurata per usare pagine di errore personalizzate, si otterrà una schermata simile a quella mostrata nella figura 6. È una schermata che ogni sviluppatore ASP.NET ha visto anche troppo spesso.

Figura 6. Pagina di errore standard

Quando si genera un'eccezione, si accede al tag HTML effettivo per la schermata corrispondente e il tag viene salvato nella proprietà WebHostHtmlMessage della classe Error. Quando si visita la pagina che mostra le informazioni dettagliate su una particolare eccezione, se l'istanza Error corrispondente possiede un valore nella propria proprietà WebHostHtmlMessage, al visitatore viene presentato un collegamento ad una pagina che mostra la schermata effettiva delle informazioni sull'eccezione (come quella mostrata nella figura 6). La cosa particolare in questo caso non è solo che l'errore viene registrato ma che è anche possibile visitare in una fase successiva la pagina di errore originale generata da ASP.NET. E tutto questo con gli errori personalizzati attivati.

Anche la classe Error possiede dei metodi per la serializzazione e la deserializzazione del proprio stato in e da un formato XML. Per ulteriori dettagli, vedere FromXml e ToXml nel codice disponibile con l'articolo.

La classe ErrorLogEntry: associare un errore ad un ErrorLog

L'ultima classe del sottosistema per la registrazione degli errori è la classe ErrorLogEntry, la quale associa un'istanza Error ad un'istanza ErrorLog. Quando il sottosistema del gestore HTTP chiama il metodo GetError() per recuperare informazioni circa una particolare eccezione, il metodo GetError() recupera le informazioni da un archivio di backup specifico e popola le informazioni in un'istanza ErrorLogEntry. La classe ErrorLogEntry contiene tre proprietà:

  • Id: ID univoco dei dettagli dell'eccezione.
  • Log: riferimento all'istanza ErrorLog che rappresenta l'archivio di backup.
  • Error: istanza della classe Error popolata con i dettagli dell'errore specifico.

Mentre il metodo GetError() restituisce una singola istanza ErrorLogEntry, il metodo GetErrors() restituisce un elenco di istanze ErrorLogEntry. GetErrors() è stato progettato specificatamente per restituire n record di errori alla volta per pagina.

La figura 7 mostra una visualizzazione aggiornata dell'architettura ELMAH, mostrando maggiori dettagli nel sottosistema per la registrazione degli errori.

Figura 7. Architettura aggiornata

Il sottosistema del modulo HTTP

ELMAH consiste di due moduli HTTP: ErrorLogModule e ErrorMailModule. ErrorLogModule è un modulo HTTP che crea un gestore eventi per l'evento Error dell'applicazione. Nel caso di un'eccezione non gestita, il modulo HTTP ottiene il registratore degli errori appropriato come specificato nella configurazione dell'applicazione e vi chiama il metodo Log() passando un'istanza Error popolata con le informazioni sull'eccezione e con HttpContext per la richiesta corrente. Il codice sorgente seguente mostra il codice pertinente dalla classe ErrorLogModule:

public class ErrorLogModule : IHttpModule
{
    public virtual void Init(HttpApplication application)
    {
        application.Error += new EventHandler(OnError);
    }

    protected virtual ErrorLog ErrorLog
    {
        get { return ErrorLog.Default; }
    }

    protected virtual void OnError(object sender, EventArgs args)
    {
        HttpApplication application = (HttpApplication) sender;
        LogException(application.Server.GetLastError(), 
          application.Context);
    }

    protected virtual void LogException(Exception e, 
      HttpContext context)
    {
        try
        {
            this.ErrorLog.Log(new Error(e, context));
        }
        catch (Exception localException)
        {
            Trace.WriteLine(localException);
        }
    }
}

L'esecuzione di ErrorLogModule ha inizio nel metodo Init(), dove indica al runtime ASP.NET di richiamare il metodo OnError() ogni volta che viene generato l'evento Error. Il metodo OnError() fa riferimento all'oggetto HttpApplication e chiama il metodo LogException(), passandovi i dettagli dell'ultima eccezione così come l'istanza HttpContext specifica della particolare richiesta. LogException() chiama semplicemente il metodo Log() della classe ErrorLog appropriata, passandovi una nuova istanza Error (il costruttore dell'istanza Error include un'istanza Exception e HttpContext, popolandone le proprietà in modo appropriato. Per ulteriori informazioni, fare riferimento al codice sorgente scaricabile da questo articolo).

ErrorLogModule contiene una proprietà ErrorLog di sola lettura e restituisce l'istanza ErrorLog restituita da ErrorLog.Default. Default è una proprietà statica di tipo ErrorLog nella classe ErrorLog. Consulta la configurazione dell'applicazione Web per determinare quale classe utilizzare per la registrazione dell'eccezione: SqlErrorLog, MemoryErrorLog o una classe di registrazione delle eccezioni personalizzata.

Nota   Nella sezione Aggiunta di ELMAH ad un'applicazione Web ASP.NET esamineremo come configurare un'applicazione Web per utilizzare un registratore delle eccezioni specifico. Ciò è semplice come aggiungere un paio di righe ai file Web.config o machine.config.

Il secondo modulo HTTP del sottosistema del modulo HTTP è la classe ErrorMailModule, che invia un messaggio di posta elettronica all'amministratore nel caso si verifichi un'eccezione. Non ci occuperemo di questa parte di ELMAH, ma è possibile analizzare come utilizzare questo modulo attraverso gli esempi di codice scaricabili da questo articolo.

Il sottosistema del gestore HTTP

Lo scopo dei gestori HTTP è quello di rendere il contenuto per un tipo particolare di risorsa. Quando nella pipeline HTTP di ASP.NET giunge una richiesta, il modulo di gestione ASP.NET esamina il percorso richiesto e stabilisce quale gestore HTTP utilizzare per gestire la risorsa richiesta. In particolare, è possibile configurare un'applicazione ASP.NET in modo che abbia un percorso particolare gestito da un gestore HTTP o da un factory di gestori HTTP. Un factory di gestori HTTP è una classe che non è direttamente responsabile di rendere il contenuto ma lo è nel selezionare e restituire un'istanza del gestore HTTP. Tale gestore HTTP restituito sarà quello a cui è stato affidato il compito di rendere la risorsa richiesta.

Il sottosistema del gestore HTTP di ELMAH consiste di un numero di classi di gestori HTTP progettate per produrre tag per visualizzare gli errori registrati insieme ad una singola classe factory di gestori HTTP. La classe factory di gestori HTTP, ErrorLogPageFactory, esamina la parte PathInfo dell'URL richiesto per stabilire quale sia il gestore HTTP che genera l'output.

Nota   La parte PathInfo di un URL è un contenuto aggiuntivo che segue il nome del file ed è disponibile mediante la proprietà PathInfo dell'oggetto Request. Ad esempio, nell'URL http://www.example.com/someDir/somePage.aspx/somePath, somePath rappresenta la parte PathInfo dell'URL. Per ulteriori informazioni sulla terminologia utilizzata per le varie parti di un URL e le proprietà dell'oggetto Request corrispondente, fare riferimento alla voce blog Making Sense of ASP.NET Paths di Rick Strahl.

Il pezzo di codice seguente mostra il codice più interessante della classe factory di gestori HTTP ErrorLogPageFactory.

public class ErrorLogPageFactory : IHttpHandlerFactory
{
    public virtual IHttpHandler GetHandler(HttpContext context, 
      string requestType, string url, string pathTranslated)
    {
        string resource = 
          context.Request.PathInfo.Length == 0 ? string.Empty :
            context.Request.PathInfo.Substring(1);

        switch (resource.ToLower(CultureInfo.InvariantCulture))
        {
            case "detail" :
                return new ErrorDetailPage();

            case "html" :
                return new ErrorHtmlPage();

            case "rss" :
                return new ErrorRssHandler();

            default :
                return new ErrorLogPage();
        }
    }
}

Potete osservare che il metodo GetHandler() della classe ErrorLogPageFactory restituisce un'istanza del gestore HTTP basata sulla parte PathInfo della richiesta. Se PathInfo è rss, viene restituita un'istanza del gestore HTTP ErrorRssHandler il quale rende la registrazione come una versione RSS. Se PathInfo è detail viene restituita un'istanza del gestore HTTP ErrorDetailPage che visualizza le informazioni su un'eccezione particolare.

Nelle impostazioni dell'applicazione Web ASP.NET, occorre specificare un percorso associato al factory di gestori HTTP ErrorLogPageFactory come ErrorLog.aspx. Per visualizzare la versione RSS del registro delle eccezioni, visitare il sito all'indirizzo www.example.com/ErrorLog.aspx/rss.

Le varie classi di gestori HTTP di ELMAH, ErrorDetailPage, ErrorHtmlPage, ErrorRssHandler, ErrorLogPage e così via, rendono tag diversi. Il gestore HTTP ErrorRssHandler, ad esempio, scorre in ciclo gli ultimi 15 errori ed emette il tag XML appropriato per visualizzare le informazioni in formato RSS. Gli altri gestori HTTP derivano tutti, direttamente o indirettamente, dalla classe System.Web.UI.Page (che è la classe da cui provengono tutte le classi del codice sottostante ASP.NET). Tali gestori HTTP relativi alle pagine sovrascrivono i metodi Render() e OnLoad() della classe Page per creare un'interfaccia HTML che visualizzi un elenco delle eccezioni registrate in forma di pagina. Per le schermate relative a queste pagine, fare riferimento alle figure 1, 2 e 3.

Nota   Mentre la classe Error salva gli insiemi ServerVariables, QueryString, Form e Cookie, solo l'insieme ServerVariables viene mostrato nei dettagli di un'eccezione. Ciò accade perché i parametri e i cookie di QueryString sono visualizzabili rispettivamente mediante i parametri QUERY_STRING e HTTP_COOKIE di ServerVariable. L'insieme Form viene omesso, poiché potrebbe contenere decine di kilobyte di informazioni relative allo stato di visualizzazione non rilevanti ai fini della diagnosi. È ovviamente possibile modificare i dettagli del gestore HTTP in modo da includere queste informazioni.

Ora che abbiamo esaminato i tre sottosistemi di ELMAH, occupiamoci di come aggiungere ELMAH ad un'applicazione Web ASP.NET esistente. Si ponga particolare attenzione a come sia semplice aggiungere ELMAH ad ogni sito: un vantaggio della componentizzazione dato dai gestori e moduli HTTP.

Aggiunta di ELMAH ad un'applicazione Web ASP.NET

Aggiungere ELMAH ad un'applicazione Web ASP.NET è piuttosto semplice e consiste di due passaggi:

  • Aggiungere l'assembly ELMAH all'applicazione Web.
  • Configurare l'applicazione Web per utilizzare i moduli HTTP e i gestori HTTP di ELMAH.

È possibile applicare ELMAH ad un'applicazione Web particolare di un server Web copiando l'assembly nella directory /bin dell'applicazione Web e configurando le impostazioni di ELMAH mediante il file Web.config. È inoltre possibile configurare ELMAH in modo che si possa applicare a tutte le applicazioni Web di un server Web aggiungendo l'assembly alla cache dell'assembly globale (GAC) del server Web ed aggiungendo le stesse impostazioni di configurazione in machine.config invece che in Web.config.

Nel file Web.config (o machine.config) occorre aggiungere le seguenti impostazioni:

  • Un elemento <sectionGroup> nell'elemento <configSections> che definisce il nome di una nuova sezione, <gotdotnet.elmah>, con una sezione interna chiamata <errorLog>, che contiene informazioni su come registrare le informazioni sull'eccezione.
  • Una sezione <gotdotnet.elmah> con una sezione interna chiamata <errorLog>, che contiene un riferimento di tipo al registratore delle eccezioni che si desidera fare utilizzare da ELMAH insieme ad ogni altra impostazione specifica per quel registratore.
  • Una voce nella sezione <httpHandlers> che indica il percorso che, quando visitato con un browser, renderà le varie visualizzazioni nel registro degli errori.
  • Una voce nella sezione <httpModules> che aggiunge ErrorLogModule alla pipeline HTTP di ASP.NET.

Il pezzo di codice seguente estratto dal file Web.config che è possibile scaricare da questo articolo, mostra come specificare queste quattro impostazioni:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- Allows for a new section group to the Web.config -->
    <sectionGroup name="gotdotnet.elmah">
      <!-- Indicates that inside the section group there will be an
              errorLog section -->
      <section name="errorLog" 
        type="System.Configuration.SingleTagSectionHandler, 
          System, Version=1.0.5000.0, Culture=neutral, 
          PublicKeyToken=b77a5c561934e089" />
    </sectionGroup>
  </configSections>

  <!-- This section group contains the type of the exception logger
         to use (SqlErrorLog, MemoryErrorLog, or a custom logger).
         It also contain properties pertinent to the exception logger
         (connectionString, for the SqlErrorLog). -->
  <gotdotnet.elmah>
    <errorLog type="GotDotNet.Elmah.SqlErrorLog, 
      GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
      PublicKeyToken=978d5e1bd64b33e5" 
      connectionString="...connection string..." />
  </gotdotnet.elmah>

  <system.web>
    <!-- Register that a request to aspnetham/errorlog.aspx should
        be serviced by the ErrorLogPageFactory HTTP Handler factory -->
    <httpHandlers>
      <add verb="POST,GET,HEAD" path="elmah/default.aspx" 
        type="GotDotNet.Elmah.ErrorLogPageFactory, 
        Skybow.Samples.AspNetHam, Version=1.0.5527.0, 
        Culture=neutral, PublicKeyToken=978d5e1bd64b33e5" />
    </httpHandlers>

    <!-- Adds the ErrorLogModule HTTP Module to the HTTP pipeline. -->
    <httpModules>
      <add name="ErrorLog" type="GotDotNet.Elmah.ErrorLogModule, 
         GotDotNet.Elmah, Version=1.0.5527.0, Culture=neutral, 
         PublicKeyToken=978d5e1bd64b33e5" />
    </httpModules>
    
    ...
  </system.web>
</configuration>

L'elemento <sectionGroup> nell'elemento <configSections> indica l'esistenza di un gruppo di sezione aggiuntivo nel file di configurazione chiamato <gotdotnet.elmah>. Indica, inoltre, che all'interno di questa sezione personalizzata c'è una sezione <errorLog>. All'interno dell'elemento <gotdotnet.elmah> effettivo c'è un elemento <errorLog> che specifica l'implementazione del registro degli errori da utilizzare. Si ricordi che ELMAH comprende due implementazioni incorporate chiamate SqlErrorLog e MemoryErrorLog. Nell'elemento <errorLog> è possibile specificare quale delle due utilizzare, o specificare di utilizzare un registratore delle eccezioni personalizzato. L'elemento <errorLog>, inoltre, contiene le impostazioni specifiche ad una implementazione del registro degli errori. Ad esempio, quando si utilizza l'elemento <errorLog> per indicare di utilizzare SqlErrorLog, occorre includere una proprietà connectionString che indica come connettersi al database. Lo script SQL per creare la tabella appropriata e le stored procedure associate è incluso nel codice che è possibile scaricare da questo articolo.

Nota   Se, nel caso si verifichi un'eccezione non gestita, si desidera notificare l'amministratore tramite posta elettronica, occorre aggiungere un altro elemento <section> nel <sectionGroup> che definisce un nuovo elemento chiamato <errorMail>. Inoltre, nell'elemento <gotdotnet.elmah> effettivo, sarà necessario aggiungere un elemento <errorMail>. Per un esempio di questa sintassi, consultare il file Web.config contenuto nel codice che è possibile scaricare da questo articolo.

La sezione <httpHandlers> specifica di utilizzare ErrorLogPageFactory (un factory di gestori HTTP) per recuperare il gestore HTTP che rende il contenuto per visualizzare il registro degli errori. Il valore dell'attributo path indica l'URL relativo della radice virtuale dell'applicazione per accedere alla visualizzazione del registro degli errori. È possibile modificarlo a piacere, ma è necessario che l'URL abbia un'estensione che possa essere gestita dal modulo di gestione ASP.NET. Se, ad esempio, si modifica il percorso in qualcosa del tipo errors.log, occorre configurare IIS in modo da associare le richieste a errors.log all'estensione ISAPI ASP.NET (aspnet_isapi.dll). Per limitare la visualizzazione del registro ai soli amministratori, utilizzare le funzionalità di autorizzazione URL per limitare l'accesso ad un utente specifico o ad un insieme di utenti o di ruoli. Se, d'altra parte, si desidera disabilitare completamente l'accesso al registro basato su Web, basta non configurare la sezione <httpHandlers>.

La sezione <httpModules> aggiunge il modulo HTTP ErrorLogModule alla pipeline HTTP di ASP.NET. È necessario includere questa impostazione <httpModules>; in caso contrario ELMAH non avvertirebbe l'evento Error, e quindi non registrerebbe alcuna eccezione non gestita.

Abbiamo mostrato quanto sia semplice aggiungere ELMAH ad una applicazione Web ASP.NET esistente. Il semplice riutilizzo e distribuzione di ELMAH è dovuto all'essere componentizzato utilizzando i moduli e i gestori HTTP.

Conclusioni

Ci auguriamo che l'articolo abbia fatto luce sulla grande potenzialità dei moduli e gestori HTTP come strumenti per la componentizzazione di funzionalità ortogonale ad un'applicazione Web ASP.NET. Mediante gestori e moduli è possibile componentizzare operazioni comuni quali la registrazione centralizzata a livello dell'applicazione o il monitoraggio di richieste sull'intera applicazione. Raccogliendo questa funzionalità in un insieme di componenti, si ottengono vantaggi in termini di riutilizzo, gestibilità e distribuzione senza che sia necessaria alcuna migrazione, integrazione o ricompilazione del codice e applicazioni esistenti.

Per dimostrare la componentizzazione possibile con i moduli e i gestori HTTP, abbiamo analizzato ELMAH, un'applicazione per la registrazione centralizzata e l'invio degli errori. ELMAH utilizza un modulo HTTP per avvertire qualsiasi evento Error a livello dell'applicazione che è generato conseguentemente alla propagazione di un'eccezione non gestita. Oltre a recepire un'eccezione non gestita, ELMAH registra l'eccezione in un database di SQL Server, nella memoria o in un altro archivio di backup. ELMAH è inoltre in grado di inviare per posta elettronica il contenuto dell'eccezione a uno o più destinatari quali gli sviluppatori e il personale operativo.

Oltre al modulo HTTP, ELMAH comprende un insieme di gestori HTTP ed un factory di gestori HTTP per facilitare la visualizzazione del registro degli errori mediante strumenti basati su Web. Oltre alle pagine Web tradizionali, ciò comprende anche una versione RSS. ELMAH gestisce un discreto componente avendo la funzionalità di visualizzazione raccolta in un gestore HTTP, invece di richiedere all'applicazione Web di includere una pagina Web ASP.NET per mostrare tali informazioni. Mediate i gestori HTTP, distribuire ELMAH è un processo semplice che non richiede la ricompilazione dell'applicazione Web o il caricamento di una pagina Web ASP.NET sul server di produzione.

ELMAH è solo un esempio della potenzialità di componentizzazione offerta dai gestori e dai moduli HTTP. Forse vi sono altri processi a livello di applicazione che possono beneficiare dalla componentizzazione mediante gestori e moduli.

Buona programmazione!

Ringraziamenti

Prima di sottoporre l'articolo alla redazione di MSDN, l'articolo è stato revisionato da un certo numero di volontari che hanno fornito il loro feedback sul contenuto, la grammatica e il senso dell'articolo. Tra i collaboratori principali per il processo di revisione, ringraziamo Milan Negovan, Carl Lambrecht, Dominique Kuster, Roman Mathis, Raffael Zaghet, Muhammad Abubakar e Patrick Schuler.

Riferimenti

Pubblicazioni correlate


Atif Aziz ha quasi 13 anni di esperienza nello sviluppo di soluzioni per la piattaforma Microsoft. È un direttore consulente per Skybow AG e il suo obiettivo principale consiste nel supportare i clienti nella comprensione e creazione di soluzioni per la piattaforma di sviluppo .NET. Atif contribuisce regolarmente alla comunità degli sviluppatori Microsoft intervenendo a conferenze Microsoft e non e scrivendo articoli per pubblicazioni di tipo tecnico. È un relatore per INETA ed è presidente di Swiss .NET User Group (dotMUGS). Può essere contattato all'indirizzo di posta elettronica atif.aziz@skybow.com o tramite il suo sito Web all'indirizzo www.raboof.com/.

Scott Mitchell, autore di cinque libri su ASP/ASP.NET e fondatore del sito 4GuysFromRolla.com, utilizza le tecnologie Web di Microsoft dal 1998. Scott lavora come consulente, insegnante e scrittore indipendente. Può essere contattato all'indirizzo di posta elettronica mitchell@4guysfromrolla o tramite il suo blog che si trova all'indirizzo scottonwriting.net/.

 

© 2004 Microsoft Corporation. Tutti i diritti riservati. Note legali.