ASP .NET 4.5 Web Forms – Tratamento de erros e registro de log – Parte 09
Neste artigo, vamos incluir o tratamento de erros e o registro de erros em nossa aplicação.
O tratamento de erros vai permitir que o aplicativo lide de forma
mais amigável com os erros, exibindo mensagens de erros adequadas em
cada situação. O registro de erros permitirá que possamos encontrar e
corrigir os erros que ocorreram durante a execução da aplicação.
O que você vai aprender:
- Como adicionar manipulação de erro global para a configuração do aplicativo;
- Como adicionar o tratamento de erros a nível de aplicação, de página e de código;
- Como registrar erros para posterior análise;
- Como exibir mensagens de erro que não comprometam a segurança;
- Como implementar módulos registro de erros e manipuladores (ELMAH) de registro de erros;
Este artigo foi integralmente baseado no artigo original:
http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/aspnet-error-handling (com algumas alterações e portado para a linguagem VB .NET).
Conceitos
As aplicações ASP.NET devem ser capaz de lidar com os erros que
ocorrem durante a execução de uma forma consistente. A ASP.NET usa
o Common Language Runtime (CLR), que fornece uma maneira de notificar as
aplicações de erros de uma maneira uniforme. Quando ocorre um erro, uma
exceção é lançada. Uma exceção é qualquer erro, condição ou
comportamento inesperado que um aplicativo encontra durante sua
execução.
Na plataforma .NET, uma exceção é um objeto que herda da
classe System.Exception. Uma exceção é lançada a partir de uma área de
código onde ocorreu um problema. A exceção é passada para a pilha de
chamadas, um lugar onde a aplicação fornece código para manipular a
exceção. Se o aplicativo não tratar a exceção, o navegador é forçado a
exibir os detalhes do erro.
Como melhor prática, lidamos com os erros ao nível do código usando
blocos try/catch /finally dentro do código. Geralmente, colocamos esses
blocos de modo que o usuário possa corrigir problemas no contexto em que
eles ocorrem. Se os blocos de tratamento de erros estiverem muito longe
de onde ocorreu o erro, torna-se mais difícil fornecer aos usuários a
informação do que ele precisa para corrigir o problema.
Classe de exceção
A classe Exception é a classe base da qual as exceções herdam. A
maioria dos objetos de exceção são instâncias de alguma classe derivada
da classe Exception, como a classeSystemException, a
classe IndexOutOfRangeException, ou a classe ArgumentNullException. A
classe Exception tem propriedades, tais como a propriedade StackTrace, a
propriedadeInnerExceptio e a propriedade Message, que fornecem
informações específicas sobre o erro que ocorreu.
Hierarquia de herança de exceção
O runtime tem um conjunto base de exceções que derivam da
classe SystemException, que ele lança quando uma exceção for encontrada.
A maioria das classes que herdam da classe de exceção, como a
classe IndexOutOfRangeException e a classe ArgumentNullException, não
implementam membros adicionais. Portanto, a informação mais importante
para uma exceção pode ser encontrada na hierarquia das exceções, o nome
da exceção, e as informações contidas no exceção.
Hierarquia manipulação de exceção
Em uma aplicação ASP.NET Web Forms, exceções podem ser tratadas com
base em uma hierarquia de tratamento específico. Uma exceção pode ser
tratada nos seguintes níveis:
- Nível de aplicação;
- Nível de página;
- Nível de código.
Quando um aplicativo lida com exceções, informações adicionais sobre a
exceção que é herdada da classe Exception muitas vezes podem ser
recuperadas e exibidas para o usuário. Além do nível de aplicativo,
página e código, você também pode lidar com exceções no nível de módulo
HTTP e/ou usando um manipulador personalizado IIS.
Tratamento de erros a nível de aplicação
Você pode lidar com erros padrão no nível do aplicativo, quer seja
modificando a configuração de seu aplicativo ou adicionando um
manipulador Application_Error no arquivoGlobal.asax de sua aplicação.
Você pode lidar com erros padrão e erros de HTTP adicionando uma
seção customErrors no arquivo Web.config. A seção customErrors permite
que você especifique uma página padrão para a qual os usuários serão
redirecionados quando ocorrer um erro. Ele também permite que você
especifique páginas individuais para erros de código específicos de
status.
A seguir, temos um exemplo de configuração feita no arquivo web.config que define o tratamento de erro neste escopo:
1
2
3
4
5
6
7
|
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="ErrorPage.aspx?handler=customErrors%20section%20-%20Web.config">
<error statusCode="404" redirect="ErrorPage.aspx?msg=404&handler=customErrors%20section%20-%20Web.config"/>
</customErrors>
</system.web>
</configuration>
|
Infelizmente, quando usamos a configuração para redirecionar o
usuário para uma página diferente, não temos os detalhes do erro que
ocorreu.No entanto, podemos capturar os erros que ocorrem em qualquer
lugar na aplicação, adicionando código para o manipulador
Application_Error no arquivo Global.asax, conforme mostra o exemplo a
seguir:
1
2
3
4
5
6
7
8
9
|
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim exc As Exception = Server.GetLastError()
If TypeOf exc Is HttpUnhandledException Then
Server.Transfer("ErrorPage.aspx?handler=Application_Error%20-%20Global.asax", True)
End If
End Sub
|
Tratamento de erro a nível de página
Um manipulador a nível de página retorna o usuário para a página onde
o erro ocorreu, mas como as instâncias dos controles não são mantidas,
não haverá mais nada na página. Para fornecer os detalhes do erro para o
usuário, devemos escrever especificamente os detalhes do erro na
página.
Devemos usar um manipulador de erro a nível de página para registrar
erros sem tratamento ou para levar o usuário para uma página que pode
exibir informações úteis.
Este exemplo de código mostra um manipulador para o evento de erro em
uma página ASP.NET. Este manipulador captura todas as exceções que não
estão sendo tratadas dentro do bloco try/catch na página.
1
2
3
4
5
6
7
8
9
10
|
Private Sub Page_Error(sender As Object, e As EventArgs)
Dim exc As Exception = Server.GetLastError()
If TypeOf exc Is HttpUnhandledException Then
ErrorMsgTextBox.Text = "Ocorreu um erro nesta página "
End If
Server.ClearError()
End Sub
|
Depois de tratar um erro, você deve limpá-lo, chamando o método
ClearError do objeto Server (classe HttpServerUtility); caso contrário,
você vai ver um erro que tenha ocorrido anteriormente.
Tratamento de erro a nível de código
A instrução try-catch consiste em um bloco try seguido de uma ou mais
cláusulas de captura, que especificam os manipuladores para exceções
diferentes. Quando uma exceção é lançada, o Common Language Runtime
(CLR) procura a instrução catch que trata esta exceção.
Se o método atualmente em execução não contém um bloco catch, a CLR
olha para o método que chamou o método atual, e assim por diante, até a
pilha de chamadas. Se nenhum bloco catch é encontrado, a CLR exibe uma
mensagem de exceção não tratada para o usuário e para a execução do
programa.
O exemplo de código a seguir mostra uma maneira comum de usar try/catch/finally para manipular erros:
1
2
3
4
5
6
7
8
9
10
11
|
Try
file.ReadBlock(buffer, index, buffer.Length)
Catch e As FileNotFoundException
Server.Transfer("NoFileErrorPage.aspx", True)
Catch e As System.IO.IOException
Server.Transfer("IOErrorPage.aspx", True)
Finally
If file IsNot Nothing Then
file.Close()
End If
End Try
|
No código acima, o bloco try contém o código que precisa ser
protegido contra uma possível exceção. O bloco é executado até que uma
exceção é lançada ou o bloco é concluído com êxito. Se uma excepção
FileNotFoundException ou uma exceção IOException ocorre, a execução é
transferida para uma página diferente. Em seguida, o código contido no
bloco finally é executado, se ocorreu um erro ou não – o código do bloco
finally sempre será executado.
Incluindo o suporte ao registro de erros
Antes de adicionar a manipulação de erros em nossa aplicação, vamos
adicionar o suporte ao registro de erros, adicionando uma classe
ExceptionUtility na pasta de Logic do nosso projeto.
Ao fazer isso, cada vez que o aplicativo tratar um erro, os detalhes do erro serão adicionados ao arquivo de log de erros.
Clique com o botão direito do mouse sobre a pasta Logic e selecione Add -> New Item;
A seguir, selecione Visual Basic -> Code e o template Class e informe o nome ExceptionUtility.vb e clique no botão Add;
A seguir, digite o código abaixo neste arquivo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.IO
Namespace WingtipToys.Logic
Public NotInheritable Class ExceptionUtility
Private Sub New()
End Sub
Public Shared Sub LogException(exc As Exception, source As String)
Dim logFile As String = "App_Data/ErrorLog.txt"
logFile = HttpContext.Current.Server.MapPath(logFile)
Dim sw As New StreamWriter(logFile, True)
sw.WriteLine("********** {0} **********", DateTime.Now)
If exc.InnerException IsNot Nothing Then
sw.Write("Inner Exception Type: ")
sw.WriteLine(exc.InnerException.[GetType]().ToString())
sw.Write("Inner Exception: ")
sw.WriteLine(exc.InnerException.Message)
sw.Write("Inner Source: ")
sw.WriteLine(exc.InnerException.Source)
If exc.InnerException.StackTrace IsNot Nothing Then
sw.WriteLine("Inner Stack Trace: ")
sw.WriteLine(exc.InnerException.StackTrace)
End If
End If
sw.Write("Exception Type: ")
sw.WriteLine(exc.[GetType]().ToString())
sw.WriteLine("Exception: " + exc.Message)
sw.WriteLine("Source: " + source)
sw.WriteLine("Stack Trace: ")
If exc.StackTrace IsNot Nothing Then
sw.WriteLine(exc.StackTrace)
sw.WriteLine()
End If
sw.Close()
End Sub
End Class
End Namespace
|
Quando ocorre uma exceção, ela pode ser gravada em um arquivo de log
de exceção chamando o método LogException. Este método tem dois
parâmetros: o objeto da exceção e uma string contendo detalhes sobre a
origem da exceção. O log de exceção é gravado no arquivo ErrorLog.txt na
pasta App_Data.
Adicionando uma página de erro
Em nossa aplicação usaremos uma página para exibir erros. A página de
erro é projetada para mostrar uma mensagem de erro segura para os
usuários do site. No entanto, se o usuário for um desenvolvedor fazendo
uma solicitação HTTP que está sendo servida localmente na máquina onde
reside o código, iremos exibir detalhes de erro adicionais na página de
erro que serão vistas apenas pelo desenvolvedor.
Clique com o botão direito do mouse sobre o nome do projeto e
selecione Add -> New Item. Selecione Visual Basic -> Web e, a
seguir, o template Web Form using Master Page e informe o
nome ErrorPage.aspx e clique no botão Add.
Selecione o arquivo Site.Master como a página principal e, em seguida, escolha OK.
A seguir, substitua a marcação existente pelo código a seguir:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="ErrorPage.aspx.vb" Inherits="WingTipToys.ErrorPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
<h2>Erro:</h2>
<p></p>
<asp:Label ID="FriendlyErrorMsg" runat="server" Text="Label" Font-Size="Large" style="color: red"></asp:Label>
<asp:Panel ID="DetailedErrorPanel" runat="server" Visible="false">
<p>
Erro Detalhado:
<br />
<asp:Label ID="ErrorDetailedMsg" runat="server" Font-Bold="true" Font-Size="Large" /><br />
</p>
<p>
Tratamento Erro:
<br />
<asp:Label ID="ErrorHandler" runat="server" Font-Bold="true" Font-Size="Large" /><br />
</p>
<p>
Mensagem de erro Detalhada:
<br />
<asp:Label ID="InnerMessage" runat="server" Font-Bold="true" Font-Size="Large" /><br />
</p>
<pre>
<asp:Label ID="InnerTrace" runat="server" />
</pre>
</asp:Panel>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
</asp:Content>
|
No arquivo code-behind ErrorPage.aspx.vb inclua o código abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports WingTipToys.WingtipToys.Logic
Public Class ErrorPage
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim generalErrorMsg As String = "Ocorreu um problema na aplicação web. Tente novamente. " + "Se o ocorre persistir contacte o suporte técnico."
Dim httpErrorMsg As String = "Ocorreu um erro HTTP. Página não encontrada. Tente novamente."
Dim unhandledErrorMsg As String = "O erro não foi tratado pelo código da aplicação."
FriendlyErrorMsg.Text = generalErrorMsg
Dim errorHandler__1 As String = Request.QueryString("handler")
If errorHandler__1 Is Nothing Then
errorHandler__1 = "Error Page"
End If
Dim ex As Exception = Server.GetLastError()
Dim errorMsg As String = Request.QueryString("msg")
If errorMsg = "404" Then
ex = New HttpException(404, httpErrorMsg, ex)
FriendlyErrorMsg.Text = ex.Message
End If
If ex Is Nothing Then
ex = New Exception(unhandledErrorMsg)
End If
If Request.IsLocal Then
ErrorDetailedMsg.Text = ex.Message
ErrorHandler.Text = errorHandler__1
DetailedErrorPanel.Visible = True
If ex.InnerException IsNot Nothing Then
InnerMessage.Text = ex.[GetType]().ToString() + "<br/>" + ex.InnerException.Message
InnerTrace.Text = ex.InnerException.StackTrace
Else
InnerMessage.Text = ex.[GetType]().ToString()
If ex.StackTrace IsNot Nothing Then
InnerTrace.Text = ex.StackTrace.ToString().TrimStart()
End If
End If
End If
ExceptionUtility.LogException(ex, errorHandler__1)
Server.ClearError()
End Sub
End Class
|
Quando a página de erro for exibida, o evento Page_Load é executado.
No manipulador Page_Load, o local onde o erro foi primeiro tratado é
determinado. Então, o último erro que ocorreu é obtido através do método
GetLastError do objeto Server.
Se a exceção não existir mais, uma exceção genérica será criada.
Então, se o pedido HTTP foi feita localmente, todos os detalhes do erro
serão mostrados. Neste caso, apenas a máquina local executando a
aplicação web vai ver esses detalhes do erro. Depois que as informações
do erro forem exibidas, o erro é adicionado ao arquivo de log e o erro é
eliminado do servidor.
Exibindo mensagens de erro não tratadas para a aplicação
Ao adicionar uma seção customErrors no arquivo Web.config, você pode
rapidamente tratar erros simples que ocorrem na aplicação. Você também
pode especificar como lidar com erros com base no seu valor de código de
status, como 404 – Arquivo não encontrado.
Abra o arquivo web.config da raiz do projeto e inclua a seção customErrors no interior do nó conforme abaixo:
A seção customErrors especifica o modo, o que é definido como “On”
(mode=”On”). Também especifica o defaultRedirect, que diz à aplicação
para qual página navegar quando ocorrer um erro.
Além disso, adicionamos um elemento de erro específico que define
como lidar com um erro 404 quando uma página não for encontrada. Mais
adiante, vamos adicionar o tratamento de erro adicional que irá capturar
os detalhes de um erro a nível de aplicação.
Testando a aplicação
Execute a aplicação pressionando CTRL+F5 (ou clicando no botão do
menu que executa a aplicação). O navegador irá abrir e apresentar a
página Default.aspx:
Informe a seguinte URL no navegador para provocar um
erro: http://localhost:674846/NoPage.aspx (o número da porta para sua
aplicação será diferente).
A página de erro ErrorPage.aspx será exibida conforme a figura a seguir:
Quando você solicita a página NoPage.aspx que não existe, a página de
erro irá mostrar a mensagem de erro simples e as informações de erro
detalhadas (se detalhes adicionais estiverem disponíveis).
No entanto, se o usuário solicitou uma página inexistente a partir de
um local remoto, a página de erro só vai mostrar a mensagem de erro em
vermelho.
Incluindo uma exceção para testes
Para verificar como o aplicativo irá funcionar quando ocorre um erro,
você pode deliberadamente criar condições de erro. Vamos lançar uma
exceção de teste quando a página padrão for carregada para ver o que
acontece.
Abra o arquivo code-behind Default.aspx.vb e no evento Load da página inclua o código abaixo:
1
2
3
4
5
6
7
|
Public Class _Default
Inherits Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Throw New InvalidOperationException("O Erro : InvalidOperationException " + "õcorreu no evento Page_Load ná página Default.aspx.")
End Sub
End Class
|
No código acima estamos criando uma exceção do
tipo InvalidOperationException, que irá ocorrer quando a
página Default.aspx for carregada.
Testando a aplicação
Execute a aplicação novamente pressionando CTRL+F5 (ou clicando no
botão do menu que executa a aplicação). O navegador irá abrir e
apresentar a página Default.aspx.
A página exibirá a mensagem de erro conforme abaixo:
Adicionando o tratamento de erros a nível de aplicativo
Em vez de capturar a exceção usando a seção customErrors no
arquivo Web.config, onde você ganha pouca informação sobre a exceção,
você pode interceptar o erro no nível da aplicação e recuperar detalhes
do erro.
Abra o arquivo Global.asax e atualize o código do evento Application_Error conforme abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
|
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim exc As Exception = Server.GetLastError()
If TypeOf exc Is HttpUnhandledException Then
If exc.InnerException IsNot Nothing Then
exc = New Exception(exc.InnerException.Message)
Server.Transfer("ErrorPage.aspx?handler=Application_Error%20-%20Global.asax", True)
End If
End If
End Sub
|
Quando ocorre um erro na aplicação, o manipulador Application_Error é
chamado. Nesse manipulador, a última exceção é recuperada e revisada.
Se a exceção ocorreu sem um tratamento e a exceção contém detalhes de
uma exceção interior (InnerException não é nulo), a aplicação transfere a
execução para a página de erro onde os detalhes da exceção são
exibidos.
Testando a aplicação
Execute a aplicação novamente pressionando CTRL+F5 (ou clicando no
botão do menu que executa a aplicação). O navegador irá abrir e
apresentar a página Default.aspx.
A página exibirá a mensagem de erro conforme abaixo:
Adicionando o tratamento de erros a nível de página
Podemos adicionar a manipulação de erros a nível de página para uma
página, seja adicionando um atributo ErrorPage na diretiva @Page da
página, ou pela adição de um manipulador de eventos Page_Error ao
código-behind da página. Vamos adicionar um manipulador de
eventos Page_Error que irá transferir a execução para a
página ErrorPage.aspx.
Abra o arquivo code-behind Default.aspx.vb e inclua o código para o evento Page_Error() conforme abaixo:
1
2
3
4
5
6
7
8
9
10
|
Private Sub Page_Error(sender As Object, e As EventArgs)
Dim exc As Exception = Server.GetLastError()
If TypeOf exc Is InvalidOperationException Then
Server.Transfer("ErrorPage.aspx?handler=Page_Error%20-%20Default.aspx", True)
End If
End Sub
|
Quando ocorre um erro na página, o manipulador de eventos Page_Error é
chamado. Nesse manipulador, a última exceção é recuperada e revisada.
Se um erro do tipo InvalidOperationException ocorrer, o manipulador de
eventos Page_Error transfere a execução para a página de erro onde os
detalhes da exceção são exibidos.
Testando a aplicação
Execute a aplicação novamente pressionando CTRL+F5 (ou clicando no
botão do menu que executa a aplicação). O navegador irá abrir e
apresentar a página Default.aspx.
A página exibirá a mensagem de erro conforme abaixo:
Após essa bateria de testes, remova ou comente o código usado no
evento Load da página Default.aspx para lançar deliberadamente um erro.
Usando ELMAH
ELMAH (módulos de registro de erros e manipuladores) é uma unidade de
registro de erros que você conecta em sua aplicação ASP.NET como um
pacote NuGet. A ELMAH oferece os seguintes recursos:
- Registro de exceções não tratadas;
- Uma página da Web para exibir todo o log de exceções não tratadas;
- Uma página da Web para exibir os detalhes de cada exceção logada;
- Uma notificação por e-mail de cada erro no momento em que ocorre;
- Um feed RSS dos últimos 15 erros do registro.
Antes de trabalhar com o ELMAH, temos que instalá-lo. Isto é fácil usando o instalador de pacotes NuGet.
Obs: O NuGet é uma extensão do Visual Studio que faz
com que seja fácil de instalar e atualizar bibliotecas de código aberto
e ferramentas no Visual Studio.
Com o projeto aberto no Visual Web Developer for Web, clique no
menu TOOLS e a seguir selecione Library Package Manager -> Manage
Nuget Packages for Solution.

A janela - Manage Nuget Packages - será exibida. Selecione o
item OnLine e digite na caixa de busca a palavra ELMAH. O
pacote ELMAH deverá ser exibido no topo da lista de itens encontrados.
Clique no botão Install para instalar o pacote ELMAH. A instalação
apresentará uma tela solicitando a confirmação para instalar o pacote no
projeto. Confirme clicando em OK.
Obs: Você deve estar conectado para poder baixar o pacote.
Se for solicitado, recarregar os arquivos abertos, selecione “Sim para Todos” (Yes to All).
O pacote ELMAH adiciona entradas na seção <modules> do
nó <system.webServer> no arquivo Web.config. Se o sistema
solicitar se você deseja recarregar o arquivo Web.config modificado,
clique em Sim(Yes).
Com essa etapa concluída, estamos pronto para usar o pacote ELMAH.
Visualizando o log ELMAH
Ver o log ELMAH é fácil, mas primeiro vamos criar uma exceção não tratada que será gravada no log ELMAH.
Pressione CTRL + F5 para executar nossa aplicação. A seguir, digite a
seguinte URL (usando o seu número de porta) no navegador para provocar
uma exceção não tratada no log ELMAH: localhost:64786/NoPage.aspx.
A página de erro será exibida conforme abaixo:
Para visualizar o log de erros ELMAH digite a url no navegador: http://localhost:64786/elmah.axd
O log de erros ELMAH será exibido indicando informações sobre a ocorrência:
Se você clicar no link Details, irá obter os detalhes da exceção:
Atenção!!! Cuidado ao usar o recurso ELMAH, pois
qualquer um pode ler o código de erro acessando a página, e
existem exploits (um exploit é um programa de computador ou uma
sequência de comandos que se aproveita das vulnerabilidades de um
sistema) que exploram isso.