Compartilhar via


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 oErrorevento é 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.

Sceenshot que destaca o arquivo Global dot A S A X.

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.asaxe 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_Errormanipuladores de eventos , Application_Start, Session_Start, Application_Ende 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_EventNamede 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, Bodye 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 MailMessagearquivo . 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=fooo . 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).

Captura de tela que mostra o e-mail enviado ao desenvolvedor.

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)

Captura de tela que mostra que a notificação por e-mail inclui os detalhes da exceção Y S O D como um anexo.

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).

Captura de tela que mostra que o navegador é redirecionado quando ocorre um erro.

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: