asp - asp.net - aspcode.it

COMMUNITY - Login
 Username:
 
 Password:
 
Voglio registrarmi!
Password dimenticata?
 Utenti on-line: 0
 Ospiti on-line: 5843
ASPCode.it - Store

  > > Articoli

La gestione degli errori in ASP.NET (1/2)

Data di pubblicazione: 09/12/2003        Voto della community: 2,83 (Votanti: 2)


Un pò di tempo fa mi resi conto che non stavo sfruttando al massimo le facilities per la gestione degli errori di ASP.NET. Il massimo delle mie conoscenze fino ad allora erano il nuovo costrutto (per Visual Basic) Try ... Catch ... Finally a livello di pagina. Mentre l'articolo si occupera' di questa novità, ci sono altre facilities a nostra disposizione e con l'articolo condividerò i risultati delle mie recenti ricerche.
Anche nel caso più semplice, la vostra applicazione molto probabilmente conterrà errori. Dovrete perciò identificare dove gli errori si verificheranno con più probabilità e scrivere codice per anticiparli e gestirli con eleganza.
Il .NET Framework Common Language RunTime (CLR) implementa la gestione delle eccezioni come una delle caratteristiche fondamentali. Come potreste aspettarvi grazie all'indipendenza del linguaggio del framework, potete anche scrivere codice in VB.NET che gestisce errori in codice C#. L'oggetto lanciato dall'eccezione è dello stesso tipo primitivo (System.Exception).

Le Option per prevenire gli errori runtime

Quando possiamo dovremmo catturare gli errori prima possibile in modo da lasciare passare meno errori possibili all'ambiente runtime. VB.NET offre le istruzioni Option Strict e Option Explicit per prevenire errori in fase di design.
I programmatori in ASP classico dovrebbero essere familiari con l'Option Explicit - essa forza le dichiarazioni esplicite di tutte le variabili a livello di modulo. In aggiunta, quando programmiamo pagine ASP.NET in VB.NET quando l'Option Explicit è abilitata, è necessario dichiarare tutte le variabili usando Pubic, Private, Dim o Redim.
L'ovvio errore che l'Optin Explicit cattura è l'uso dello stesso nome di variabile più volte all'interno dello stesso scope... una situazione che molto probabilmente conduce ad errori runtime, se non ad eccezioni.
Forunatamente, in ASP.NET l'Option Explicit è abilitata di defualt. Se, per qualsiasi ragione, avreste bisogno di resettarla, la sintassi è:

Option Explicit Off

al top del codice del modulo oppure

<%@ Page Explicit=”False”%>

in una web form.

Abilitando l'Option Strict vengono generati errori se si tenta di effettuare una conversione fra tipi che condurrebbe ad una perdita nei dati. Perciò, se questa perdita di dati è potenzialmente importante per la vostra applicazione, dovrebbe essere abilitato Option Strict. Option Strict consente conversioni di "allargamento", dove il type del target è capace di contenere un ammontare di dati più grande di quello di cui si effettua la conversione.
La sintassi è la stessa di Option Explicit.

Eccezioni

Che cosa è precisamente una eccezione? La classe Exception è un membro del Namespace System e la classe base per tutte le eccezioni. Le sue due sub-classi sono la classe SystemException e la classe ApplicationException.
La classe SystemException definisce la classe base per tutte le eccezioni predefinite dal .NET framework. Una che incontro frequentemente è la classe SQLException, tipicamente quando non ho specificato i parametri della mia stored procedure in conformità con la stored procedure stessa.
Quando è generato un oggetto exception che deriva dalla classe System.Exception, possiamo ottenere informazioni su di essa riguardo l'eccezione che si è verificata. Ad esempio, sono esposte le seguenti proprietà:

- HelpLink: riceve o setta un link al file di help associato a questa eccezione.

- InnerException: riceve l'istanza di Exception che ha causato l'eccezione corrente

- Message: riceve un messaggio che descrive l'eccezione corrente

- Source: riceve o setta il nome dell'applicazione o dell'oggetto che ha causato l'errore

- StakTrace: riceve una rappresentazione in stringa dei frame dello stack al momento in cui è stata generata l'eccezione.

- TargetSite: riceve il metodo che lancia l'eccezione corrente.

Consultare la documentazione SDK per maggiori informazioni.

La classe ApplicationException permette di definire nostre eccezioni personalizzate. Contiene le stesse proprietà e metodi della classe SystemException. Torneremo ad esaminare tali eccezioni personalizzate dopo aver esaminato il principale costrutto per la gestione degli errori a nostra disposizione: 'Try...Catch...Finally'.

Error Handling stutturato e non strutturato

Le precedenti versioni di VB possedevano soltanto error handling non strutturato. Questo metodo consiste nell'usare un singolo error handler all'interno di un metodo che cattura tutte le eccezioni. E' poco pulito e limitato. Brevemente, gli error handler non strutturati di VB (ancora supportati) sono:

On Error GoTo line [oppure] label
On Error Resume Next
On Error GoTo 0
On Error GoTo -1

Ma dimenticatevi di questi perché ora abbiamo una nuova e più potente gestione degli errori stile C++ con il costrutto Try....Catch...Finally, come segue:

Try
  [ tryStatements ]
[ Catch [ exception [ As type ] ] [ When expression ]
  [ catchStatements ] ]
[ Exit Try ]
...
[ Finally
  [ finallyStatements ] ]
End Try

Concretamente, il sistema prova ad eseguire del codice; se questo codice genera una eccezione il runtime controllerà se l'eccezione è gestita da uno dei blocchi Catch nell'ordine. Infine possiamo eseguire del codice di "pulitura" appropriato. Exit Try eventualmente ci permette di abbandonare il costrutto e continuare l'esecuzione del codice dopo End Try. When consente eventualmente di specificare una condizione aggiuntiva che deve essere vera perché il blocco Catch possa essere eseguito.

Ecco un frammento di codice per operazioni SQL Server tratto da una mia semplice applicazione, ma non particolarmente ottimizzato (vedere il commento successivo):

Try

  myConnection.open()
  myCommand = new SQLCommand("USP_GENERIC_select_event_dates", myConnection)
  myCommand.CommandType = CommandType.StoredProcedure

  myCommand.Parameters.Add(New SQLParameter("@EventId",SQLDBType.int))
  myCommand.Parameters("@EventId").value=EventId

  objDataReader=myCommand.ExecuteReader()

Catch objError As Exception

 
'display error details
  outError.InnerHtml = "<b>* Error while executing data command (ADMIN: Select Event Dates)</b>.<br />" _
  & objError.Message & "<br />" & objError.Source & _
  ". Please <a href='mailto:mascymru@cymru-web.net'>e-mail us</a> providing as much detail as possible including the error message, what page you were viewing and what you were trying to achieve.<p /><p />"

  Exit Function
' and stop execution

End Try


Il codice presenta diversi problemi dal punto di vista della ottimizzazione, il più generale dei quali lascio al lettore cogliere da quanto dirò più avanti, ma in particolare dovrebbe esserci una sezione Finally che chiude gli oggetti del database.
Notare che è buona norma avere multipli blocchi 'Catch' per catturare differenti tipi di eccezioni possibili. L'ordine dei blocchi Catch influenza il risultato... sono infatti controllati nell'ordine.
Possiamo anche generare nostre eccezioni all'interno del costrutto; oppure rilanciare esistenti eccezioni in modo che vengano gestite altrove (consultare la prossima sezione per maggiori dettagli).
Potremmo avere solamente un blocco Catch che catturi le eccezioni generali  - exception di tipo 'exception' (vedere sopra). Questa pratica non è raccomandata. Il blocco Catch iniziale dovrebbe essere per possibili errori specifici, mentre un blocco catch per le eccezioni generali dovrebbe essere l'ultima via d'uscita per le eccezioni non previste dai blocchi precedenti.
Per esempio, accedendo ad SQLServer, sappiamo che è possibile una eccezione SQLException. Se sappiamo che un oggetto puo' resitutire un valore null e causare una eccezione, possiamo gestire con eleganza l'eccezione scrivendo una specifica istruzione Catch per NullReferenceException.

Ecco una lista dei tipi predefiniti di eccezione forniti dal .NET Runtime:

Tipo di eccezione Tipo base Descrizione Esempio
Exception Object Classe base per tutte le eccezioni. Nessuno (usare una classe derivata da questa eccezione).
SystemException Exception Classe base per tutti gli errori generati a runtime. Nessuno (usare una classe derivata da questa eccezione).
IndexOutOfRange_
Exception
SystemException Generata dal runtime soltanto quando è riferito un elemento dell'array impropriamente Puntare un array al di fuori del suo range valdio: arr[arr.Length+1]
NullReferenceException SystemException Generato dal runtime solo quando è referenziato un oggetto nullo object o = null; o.ToString();
InvalidOperationException SystemException Generato da metodi quando sono in uno stato invalido Chiamare Enumerator.GetNext() dopo avere rimosso un Item dalla collezione sottostante
ArgumentException SystemException Classe base per tutte le eccezioni relative all'argomento Nessuno (usare una classe derivata da questa eccezione)
ArgumentNullException ArgumentException Generata ma metodi che non consentono un argomento nullo String s = null; "Calculate".IndexOf (s);
ArgumentOutOfRange_
Exception
ArgumentException Generata da metodi che verificano che gli argomenti siano in un intervallo dato. String s = "string"; s.Chars[9];
ExternalException SystemException Classe base per le eccezioni che si verificano o sono 'targeted' in ambienti esterni al runtime. Nessuno (usare una classe derivata da questa eccezione).
ComException ExternalException Eccezione che incapsula informazioni COM HRESULT Usata in COM interop.
SEHException ExternalException Eccezione che incapsula una Win32 structured exception handling information. Usata in codice unmanaged interop.

Generare eccezioni

Come accennato prima, non solo è possibile reagire alle eccezioni generate ma è anche possibile lanciare eccezioni quando necessario. Ad esempio, potremmo voler rilanciare una eccezione dopo averla catturata e non essere stati in grado di "riparare" all'eccezione stessa. La nostra gestione dell'errore a livello di applicazione potrebbe poi ridirezionare ad una appropriata pagina di errore.
Possiamo anche lanciare nostre eccezioni personalizzate in reazione a condizioni di errore nel codice.

La sintassi da utilizzare è la seguente:

Throw new [Exception]

Creare Eccezioni personali

Come accennato, attraverso la classe ApplicationException abbiamo la possibilità di creare nostre eccezioni personalizzate. Ecco un esempio di una classe VB che assolve proprio a questo compito:

Imports System
Imports System.Text

Namespace CustomExceptions

  Public Class customException1: Inherits ApplicationException

    Public Sub New()

      MyBase.New("<H4>Custom Exception</H4><BR>")
      Dim strBuild As New StringBuilder()
      strBuild.Append("<p COLOR='RED'>")
      strBuild.Append("For more information ")
      strBuild.Append("please visit: ")
      strBuild.Append("<a href='http://www.cymru-web.net/exceptions'>")
      strBuild.Append("Cymru-Web.net</a></p>")
      MyBase.HelpLink = strBuild.ToString()

    End Sub

  End Class

End Namespace

Diamo un'occhiata al codice. Alla sesta linea notiamo che, per creare una custom exception, dobbiamo ereditare dalla classe ApplicationException. Nel codice di inizializzazione della classe costruiamo un nuovo oggetto ApplicationException usando una stringa (uno dei costruttori overloaded di ApplicationException - vedere la documentazione per gli altri). Settiamo anche la proprietà string HelpLink per l'eccezione inizializzandola con un messaggio user-friendly di presentazione per qualsiasi client application.
La keyword MyBase si comporta come una variabile oggetto che referenzia la classe base dell'istanza corrente di una classe (ApplicationException). MyBase è soliltamente usata per accedere ai membri della classe base che sono overridden o shadowed in una classe derivata. In particolare, MyBase.New è usato per chiamare esplicitamente un costruttore della classe base dal costrutore di una classe derivata.

Di seguito è presentato un piccolo client di test scritto in VB.NET utilizzando Visual Studio.NET; avremo quindi sia la webform che il file code-behind.

WebForm1.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" Inherits="article_error_handling.WebForm1"%>
<html>
<body>
</body>
</html>

WebForm1.aspx.vb

Imports article_error_handling.CustomExceptions

Public Class WebForm1

  Inherits System.Web.UI.Page

  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Try
      Throw New customException1()
    Catch ex As customException1
      Response.Write(ex.Message & " " & ex.HelpLink)
    End Try
  End Sub

End Class

Il semplice esempio proposto può essere esteso secondo le esigenze personali del lettore.


Si ringrazia DotNetJohn.com per la gentile concessione dell'articolo.




Utenti connessi: 5843