Processamento de exceções sem tratamento (C#)
por Scott Mitchell
Exibir ou baixar código de exemplo (como baixar)
Quando ocorre um erro de tempo de execução em um aplicativo Web em produção, é importante notificar um desenvolvedor e registrar o erro para que ele possa ser diagnosticado posteriormente. Este tutorial fornece uma visão geral de como o ASP.NET processa erros de runtime e analisa uma maneira de fazer com que o código personalizado seja executado sempre que uma exceção sem tratamento surgir até o runtime ASP.NET.
Introdução
Quando ocorre uma exceção sem tratamento em um aplicativo ASP.NET, ela é exibida para o ASP.NET tempo de execução, que gera o Error
evento e exibe a página de erro apropriada. Existem três tipos diferentes de páginas de erro: a Tela Amarela da Morte de Erro de Tempo de Execução (YSOD); o YSOD de detalhes da exceção; e páginas de erro personalizadas. No tutorial anterior, configuramos o aplicativo para usar uma página de erro personalizada para usuários remotos e o YSOD de Detalhes de Exceção para usuários que visitam localmente.
O uso de uma página de erro personalizada amigável que corresponda à aparência do site é preferível ao YSOD de Erro de Tempo de Execução padrão, mas exibir uma página de erro personalizada é apenas uma parte de uma solução abrangente de tratamento de erros. Quando ocorre um erro em um aplicativo em produção, é importante que os desenvolvedores sejam notificados sobre o erro para que possam descobrir a causa da exceção e resolvê-la. Além disso, os detalhes do erro devem ser registrados para que o erro possa ser examinado e diagnosticado posteriormente.
Este tutorial mostra como acessar os detalhes de uma exceção sem tratamento para que eles possam ser registrados e um desenvolvedor notificado. Os dois tutoriais seguintes a este exploram bibliotecas de log de erros que, após um pouco de configuração, notificarão automaticamente os desenvolvedores sobre erros de tempo de execução e registrarão seus detalhes.
Observação
As informações examinadas neste tutorial são mais úteis se você precisar processar exceções sem tratamento de alguma maneira exclusiva ou personalizada. Nos casos em que você só precisa registrar a exceção e notificar um desenvolvedor, usar uma biblioteca de log de erros é o caminho a percorrer. Os próximos dois tutoriais fornecem uma visão geral de duas dessas bibliotecas.
Executando código quando oError
evento é gerado
Os eventos fornecem a um objeto um mecanismo para sinalizar que algo interessante ocorreu e para outro objeto executar código em resposta. Como desenvolvedor ASP.NET, você está acostumado a pensar em termos de eventos. Se você quiser executar algum código quando o visitante clicar em um botão específico, crie um manipulador de eventos para o Click
evento desse botão e coloque seu código lá. Considerando que o runtime ASP.NET gera seu Error
evento sempre que ocorre uma exceção sem tratamento, segue-se que o código para registrar os detalhes do erro iria em um manipulador de eventos. Mas como você cria um manipulador de eventos para o Error
evento?
O Error
evento é um dos muitos eventos na HttpApplication
classe que são gerados em determinados estágios no pipeline HTTP durante o tempo de vida de uma solicitação. Por exemplo, o HttpApplication
evento da BeginRequest
classe é gerado no início de cada solicitação; seu AuthenticateRequest
evento é gerado quando um módulo de segurança identifica o solicitante. Esses HttpApplication
eventos fornecem ao desenvolvedor da página um meio de executar lógica personalizada nos vários pontos do tempo de vida de uma solicitação.
Os manipuladores de eventos para os HttpApplication
eventos podem ser colocados em um arquivo especial chamado Global.asax
. Para criar esse arquivo em seu site, adicione um novo item à raiz do seu site usando o modelo Global Application Class com o nome Global.asax
.
Figura 1: Adicionar Global.asax
ao seu aplicativo Web
(Clique para ver a imagem em tamanho real)
O conteúdo e a Global.asax
estrutura do arquivo criado pelo Visual Studio diferem ligeiramente com base no fato de você estar usando um WAP (Projeto de Aplicativo Web) ou um WSP (Projeto de Site). Com um WAP, o Global.asax
é implementado como dois arquivos separados - Global.asax
e Global.asax.cs
. O Global.asax
arquivo não contém nada além de uma @Application
diretiva que faz referência ao .cs
arquivo; os manipuladores de eventos de interesse são definidos no Global.asax.cs
arquivo. Para WSPs, apenas um único arquivo é criado Global.asax
e os manipuladores de eventos são definidos em um <script runat="server">
bloco.
O Global.asax
arquivo criado em um WAP pelo modelo de Classe de Aplicativo Global do Visual Studio inclui manipuladores de eventos chamados Application_BeginRequest
, Application_AuthenticateRequest
, e Application_Error
, que são manipuladores de eventos para os HttpApplication
eventos BeginRequest
, AuthenticateRequest
, e Error
, respectivamente. Também há manipuladores de eventos chamados Application_Start
, Session_Start
, Application_End
, e Session_End
, que são manipuladores de eventos que são acionados quando o aplicativo Web é iniciado, quando uma nova sessão é iniciada, quando o aplicativo termina e quando uma sessão é encerrada, respectivamente. O Global.asax
arquivo criado em um WSP pelo Visual Studio contém apenas os Application_Error
manipuladores de eventos , Application_Start
, Session_Start
, Application_End
e Session_End
.
Observação
Ao implantar o aplicativo ASP.NET, você precisa copiar o Global.asax
arquivo para o ambiente de produção. O Global.asax.cs
arquivo, que é criado no WAP, não precisa ser copiado para produção porque esse código é compilado no assembly do projeto.
Os manipuladores de eventos criados pelo modelo de Classe de Aplicativo Global do Visual Studio não são exaustivos. Você pode adicionar um manipulador de eventos para qualquer HttpApplication
evento nomeando o manipulador Application_EventName
de eventos . Por exemplo, você pode adicionar o seguinte código ao Global.asax
arquivo para criar um manipulador de eventos para o AuthorizeRequest
evento:
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
// Event handler code
}
Da mesma forma, você pode remover todos os manipuladores de eventos criados pelo modelo Global Application Class que não são necessários. Para este tutorial, exigimos apenas um manipulador de eventos para o Error
evento; sinta-se à vontade para remover os outros manipuladores de eventos do Global.asax
arquivo.
Observação
Os módulos HTTP oferecem outra maneira de definir manipuladores de eventos para HttpApplication
eventos. Os módulos HTTP são criados como um arquivo de classe que pode ser colocado diretamente no projeto do aplicativo Web ou separado em uma biblioteca de classes separada. Como eles podem ser separados em uma biblioteca de classes, os módulos HTTP oferecem um modelo mais flexível e reutilizável para a criação HttpApplication
de manipuladores de eventos. Embora o Global.asax
arquivo seja específico para o aplicativo Web em que reside, os módulos HTTP podem ser compilados em assemblies, momento em que adicionar o módulo HTTP a um site é tão simples quanto soltar o assembly na Bin
pasta e registrar o módulo no Web.config
. Este tutorial não examina a criação e o uso de módulos HTTP, mas as duas bibliotecas de log de erros usadas nos dois tutoriais a seguir são implementadas como módulos HTTP. Para obter mais informações sobre os benefícios dos módulos HTTP, consulte Usando módulos e manipuladores HTTP para criar componentes ASP.NET conectáveis.
Recuperando informações sobre a exceção sem tratamento
Neste ponto, temos um arquivo Global.asax com um manipulador de Application_Error
eventos. Quando esse manipulador de eventos é executado, precisamos notificar um desenvolvedor sobre o erro e registrar seus detalhes. Para realizar essas tarefas, primeiro precisamos determinar os detalhes da exceção que foi gerada. Use o método do GetLastError
objeto Server para recuperar detalhes da exceção sem tratamento que fez com que o Error
evento fosse acionado.
protected void Application_Error(object sender, EventArgs e)
{
// Get the error details
HttpException lastErrorWrapper =
Server.GetLastError() as HttpException;
}
O GetLastError
método retorna um objeto do tipo Exception
, que é o tipo base para todas as exceções no .NET Framework. No entanto, no código acima, estou convertendo o objeto Exception retornado por GetLastError
em um HttpException
objeto. Se o Error
evento estiver sendo acionado porque uma exceção foi lançada durante o processamento de um recurso ASP.NET, a exceção que foi lançada será encapsulada em um HttpException
. Para obter a exceção real que precipitou o evento Error, use a InnerException
propriedade. Se o Error
evento foi gerado devido a uma exceção baseada em HTTP, como uma solicitação para uma página inexistente, an HttpException
é gerado, mas não tem uma exceção interna.
O código a seguir usa o GetLastErrormessage
para recuperar informações sobre a exceção que acionou o Error
evento, armazenando o HttpException
em uma variável chamada lastErrorWrapper
. Em seguida, ele armazena o tipo, a mensagem e o rastreamento de pilha da exceção de origem em três variáveis de cadeia de caracteres, verificando se é lastErrorWrapper
a exceção real que disparou o Error
evento (no caso de exceções baseadas em HTTP) ou se é apenas um wrapper para uma exceção que foi lançada durante o processamento da solicitação.
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;
}
Neste ponto, você tem todas as informações necessárias para escrever um código que registrará os detalhes da exceção em uma tabela de banco de dados. Você pode criar uma tabela de banco de dados com colunas para cada um dos detalhes de erro de interesse - o tipo, a mensagem, o rastreamento de pilha e assim por diante - juntamente com outras informações úteis, como a URL da página solicitada e o nome do usuário conectado no momento. No manipulador de Application_Error
eventos, você se conectaria ao banco de dados e inseriria um registro na tabela. Da mesma forma, você pode adicionar código para alertar um desenvolvedor sobre o erro por e-mail.
As bibliotecas de log de erros examinadas nos próximos dois tutoriais fornecem essa funcionalidade pronta para uso, portanto, não há necessidade de criar esse log e notificação de erros por conta própria. No entanto, para ilustrar que o Error
evento está sendo gerado e que o manipulador de Application_Error
eventos pode ser usado para registrar detalhes de erro e notificar um desenvolvedor, vamos adicionar um código que notifica um desenvolvedor quando ocorre um erro.
Notificando um desenvolvedor quando ocorre uma exceção sem tratamento
Quando ocorre uma exceção sem tratamento no ambiente de produção, é importante alertar a equipe de desenvolvimento para que ela possa avaliar o erro e determinar quais ações precisam ser tomadas. Por exemplo, se houver um erro na conexão com o banco de dados, você precisará verificar novamente sua string de conexão e, talvez, abrir um tíquete de suporte com sua empresa de hospedagem na web. Se a exceção ocorreu devido a um erro de programação, pode ser necessário adicionar código adicional ou lógica de validação para evitar tais erros no futuro.
As classes do .NET Framework no namespace facilitam o System.Net.Mail
envio de um email. A MailMessage
classe representa uma mensagem de email e tem propriedades como To
, From
, Subject
, Body
e Attachments
. O SmtpClass
é usado para enviar um MailMessage
objeto usando um servidor SMTP especificado; as configurações do servidor SMTP podem ser especificadas programaticamente ou declarativamente no <system.net>
elemento no Web.config file
. Para obter mais informações sobre o envio de mensagens de email em um aplicativo ASP.NET, confira meu artigo, Enviando emails de um site de páginas da Web ASP.NET e System.Net.Mail.
Observação
O <system.net>
elemento contém as configurações do servidor SMTP usadas pela SmtpClient
classe ao enviar um e-mail. Sua empresa de hospedagem na web provavelmente tem um servidor SMTP que você pode usar para enviar e-mails do seu aplicativo. Consulte a seção de suporte do seu host para obter informações sobre as configurações do servidor SMTP que você deve usar em seu aplicativo Web.
Adicione o seguinte código ao manipulador de Application_Error
eventos para enviar um email a um desenvolvedor quando ocorrer um erro:
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);
}
Embora o código acima seja bastante longo, a maior parte dele cria o HTML que aparece no e-mail enviado ao desenvolvedor. O código começa referenciando o HttpException
métodolastErrorWrapper
retornado (GetLastError
). A exceção real que foi gerada pela solicitação é recuperada e lastErrorWrapper.InnerException
atribuída à variável lastError
. As informações de tipo, mensagem e rastreamento de pilha são recuperadas e armazenadas em três variáveis de cadeia de lastError
caracteres.
Em seguida, um MailMessage
objeto chamado mm
é criado. O corpo do e-mail é formatado em HTML e exibe a URL da página solicitada, o nome do usuário conectado no momento e informações sobre a exceção (o tipo, a mensagem e o rastreamento de pilha). Uma das coisas interessantes sobre a HttpException
classe é que você pode gerar o HTML usado para criar a Tela Amarela da Morte de Detalhes da Exceção (YSOD) chamando o método GetHtmlErrorMessage. Esse método é usado aqui para recuperar a marcação YSOD Detalhes da Exceção e adicioná-la ao email como um anexo. Uma palavra de cautela: se a exceção que acionou o Error
evento foi uma exceção baseada em HTTP (como uma solicitação para uma página inexistente), o GetHtmlErrorMessage
método retornará null
.
A etapa final é enviar o MailMessage
arquivo . Isso é feito criando um novo SmtpClient
método e chamando seu Send
método.
Observação
Antes de usar esse código em seu aplicativo Web, você desejará alterar os valores nas ToAddress
FromAddress
constantes e de support@example.com para qualquer endereço de email para o qual o email de notificação de erro deva ser enviado e originado. Você também precisará especificar as configurações do <system.net>
servidor SMTP na seção em Web.config
. Consulte seu provedor de host da Web para determinar as configurações do servidor SMTP a serem usadas.
Com esse código em vigor, sempre que houver um erro, o desenvolvedor receberá uma mensagem de email que resume o erro e inclui o YSOD. No tutorial anterior, demonstramos um erro de tempo de execução visitando Genre.aspx e passando um valor inválido ID
por meio da querystring, como Genre.aspx?ID=foo
. Visitar a página com o Global.asax
arquivo em vigor produz a mesma experiência do usuário que no tutorial anterior – no ambiente de desenvolvimento, você continuará a ver a Tela Amarela da Morte de Detalhes da Exceção, enquanto no ambiente de produção você verá a página de erro personalizada. Além desse comportamento existente, o desenvolvedor recebe um e-mail.
A Figura 2 mostra o e-mail recebido ao visitar Genre.aspx?ID=foo
o . O corpo do email resume as informações da exceção, enquanto o YSOD.htm
anexo exibe o conteúdo mostrado no YSOD Detalhes da Exceção (consulte a Figura 3).
Figura 2: O desenvolvedor recebe uma notificação por email sempre que há uma exceção sem tratamento
(Clique para ver a imagem em tamanho real)
Figura 3: A notificação por email inclui os detalhes da exceção YSOD como um anexo
(Clique para ver a imagem em tamanho real)
Que tal usar a página de erro personalizada?
Este tutorial mostrou como usar Global.asax
o manipulador de eventos para executar o Application_Error
código quando ocorre uma exceção sem tratamento. Especificamente, usamos esse manipulador de eventos para notificar um desenvolvedor sobre um erro; poderíamos estendê-lo para também registrar os detalhes do erro em um banco de dados. A presença do Application_Error
manipulador de eventos não afeta a experiência do usuário final. Eles ainda veem a página de erro configurada, seja o YSOD Detalhes do Erro, o YSOD de Erro de Tempo de Execução ou a página de erro personalizada.
É natural se perguntar se o arquivo e Application_Error
o Global.asax
evento são necessários ao usar uma página de erro personalizada. Quando ocorre um erro, o usuário vê a página de erro personalizada, então por que não podemos colocar o código para notificar o desenvolvedor e registrar os detalhes do erro na classe code-behind da página de erro personalizada? Embora você certamente possa adicionar código à classe code-behind da página de erro personalizada, você não tem acesso aos detalhes da exceção que disparou o Error
evento ao usar a técnica que exploramos no tutorial anterior. Chamar o GetLastError
método da página de erro personalizada retorna Nothing
.
O motivo desse comportamento é porque a página de erro personalizada é acessada por meio de um redirecionamento. Quando uma exceção sem tratamento atinge o tempo de execução ASP.NET, o mecanismo ASP.NET gera seu Error
evento (que executa o Application_Error
manipulador de eventos) e redireciona o usuário para a página de erro personalizada emitindo um Response.Redirect(customErrorPageUrl)
. O Response.Redirect
método envia uma resposta ao cliente com um código de status HTTP 302, instruindo o navegador a solicitar uma nova URL, ou seja, a página de erro personalizada. O navegador solicita automaticamente essa nova página. Você pode dizer que a página de erro personalizada foi solicitada separadamente da página em que o erro se originou porque a barra de endereços do navegador muda para a URL da página de erro personalizada (consulte a Figura 4).
Figura 4: Quando ocorre um erro, o navegador é redirecionado para a URL da página de erro personalizada
(Clique para ver a imagem em tamanho real)
O efeito líquido é que a solicitação em que ocorreu a exceção sem tratamento termina quando o servidor responde com o redirecionamento HTTP 302. A solicitação subsequente para a página de erro personalizada é uma solicitação totalmente nova; A essa altura, o mecanismo ASP.NET descartou as informações de erro e, além disso, não tem como associar a exceção sem tratamento na solicitação anterior à nova solicitação para a página de erro personalizada. É por isso que GetLastError
retorna null
quando chamado da página de erro personalizada.
No entanto, é possível que a página de erro personalizada seja executada durante a mesma solicitação que causou o erro. O Server.Transfer(url)
método transfere a execução para a URL especificada e a processa na mesma solicitação. Você pode mover o código no Application_Error
manipulador de eventos para a classe code-behind da página de erro personalizada, substituindo-o pelo Global.asax
seguinte código:
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");
}
}
Agora, quando ocorre uma exceção sem tratamento, o Application_Error
manipulador de eventos transfere o controle para a página de erro personalizada apropriada com base no código de status HTTP. Como o controle foi transferido, a página de erro personalizada tem acesso às informações de exceção sem tratamento e Server.GetLastError
pode notificar um desenvolvedor sobre o erro e registrar seus detalhes. A Server.Transfer
chamada impede que o mecanismo ASP.NET redirecione o usuário para a página de erro personalizada. Em vez disso, o conteúdo da página de erro personalizada é retornado como a resposta à página que gerou o erro.
Resumo
Quando ocorre uma exceção sem tratamento em um aplicativo Web ASP.NET, o runtime ASP.NET gera o Error
evento e exibe a página de erro configurada. Podemos notificar o desenvolvedor sobre o erro, registrar seus detalhes ou processá-lo de alguma outra forma, criando um manipulador de eventos para o evento Error. Existem duas maneiras de criar um manipulador de eventos para HttpApplication
eventos como Error
: no Global.asax
arquivo ou de um módulo HTTP. Este tutorial mostrou como criar um Error
manipulador de eventos no arquivo que notifica os Global.asax
desenvolvedores sobre um erro por meio de uma mensagem de email.
A criação de um Error
manipulador de eventos é útil se você precisar processar exceções sem tratamento de alguma maneira exclusiva ou personalizada. No entanto, criar seu próprio Error
manipulador de eventos para registrar a exceção ou notificar um desenvolvedor não é o uso mais eficiente do seu tempo, pois já existem bibliotecas de log de erros gratuitas e fáceis de usar que podem ser configuradas em questão de minutos. Os próximos dois tutoriais examinam duas dessas bibliotecas.
Boa programação!
Leitura Adicional
Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:
- Visão geral dos módulos HTTP e manipuladores HTTP do ASP.NET
- Respondendo normalmente a exceções não tratadas - Processando exceções não tratadas
HttpApplication
Classe e o objeto de aplicativo ASP.NET- Manipuladores HTTP e módulos HTTP no ASP.NET
- Enviando e-mail em ASP.NET
- Entendendo o
Global.asax
arquivo - Usando módulos e manipuladores HTTP para criar componentes ASP.NET conectáveis
- Trabalhando com o arquivo ASP.NET
Global.asax
- Trabalhando com
HttpApplication
instâncias