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 runtime 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 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 do ASP.NET.

Introdução

Quando ocorre uma exceção sem tratamento em um aplicativo ASP.NET, ela se propaga até o runtime do ASP.NET, o que aciona o Error evento e exibe a página de erro apropriada. Há três tipos diferentes de páginas de erro: o erro de runtime YSOD (tela amarela de morte); os detalhes da exceção YSOD; 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 da Exceção para usuários que visitam localmente.

Usar uma página de erro personalizada amigável que corresponda à aparência do site é preferencial para o YSOD de erro de runtime 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 a seguir exploram bibliotecas de log de erros que, após um pouco de configuração, notificarão automaticamente os desenvolvedores sobre erros de runtime e registrarão seus detalhes.

Observação

As informações examinadas neste tutorial serã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 evento desse Click Botão e coloque seu código lá. Considerando que o runtime do ASP.NET gera seu Error evento sempre que ocorre uma exceção sem tratamento, ele segue o código para registrar os detalhes do erro 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 dão ao desenvolvedor da página um meio de executar a lógica personalizada nos vários pontos no 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 classe de aplicativo global com o nome Global.asax.

Captura de tela que realça o arquivo Global dot A S A X.

Figura 1: Adicionar Global.asax ao seu aplicativo Web
(Clique para exibir a imagem em tamanho real)

O conteúdo e a estrutura do Global.asax arquivo criado pelo Visual Studio diferem ligeiramente com base em se você está 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 classe de aplicativo global do Visual Studio inclui manipuladores de eventos chamados Application_BeginRequest, Application_AuthenticateRequeste Application_Error, que são manipuladores de eventos para os HttpApplication eventos BeginRequest, AuthenticateRequeste Error, respectivamente. Também há manipuladores de eventos chamados Application_Start, Session_Start, Application_Ende 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 termina, 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 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 de eventos Application_EventName. 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 classe de aplicativo global que não são necessários. Para este tutorial, precisamos apenas de um manipulador de eventos para o Error evento; fique à 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 dentro do projeto de 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 criar HttpApplication manipuladores de eventos. Enquanto o Global.asax arquivo é específico para o aplicativo Web em que ele reside, os módulos HTTP podem ser compilados em assemblies. Nesse ponto, adicionar o Módulo HTTP a um site é tão simples quanto descartar o assembly na Bin pasta e registrar o Módulo em Web.config. Este tutorial não analisa 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 de ASP.NET conectáveis.

Recuperando informações sobre a exceção sem tratamento

Neste ponto, temos um arquivo Global.asax com um Application_Error manipulador de 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 causou a ação do Error evento.

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 disparado porque uma exceção foi gerada durante o processamento de um recurso de ASP.NET, a exceção que foi gerada será encapsulada em um HttpException. Para obter a exceção real que precipitou o evento Error, use a InnerException propriedade . Se o Error evento tiver sido gerado devido a uma exceção baseada em HTTP, como uma solicitação para uma página inexistente, um HttpException será gerado, mas não terá uma exceção interna.

O código a seguir usa o GetLastErrormessage para recuperar informações sobre a exceção que disparou 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 é a lastErrorWrapper 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 gerada 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 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. Application_Error No manipulador de 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 email.

As bibliotecas de log de erros examinadas nos próximos dois tutoriais fornecem essa funcionalidade pronta para uso, portanto, não é necessário criar esse log de erros e notificação por conta própria. No entanto, para ilustrar que o Error evento está sendo gerado e que o Application_Error manipulador de eventos pode ser usado para registrar detalhes do erro e notificar um desenvolvedor, vamos adicionar código que notifica um desenvolvedor quando ocorre um erro.

Notificar 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 executadas. Por exemplo, se houver um erro ao se conectar ao banco de dados, você precisará dobrar marcar seu cadeia 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, código adicional ou lógica de validação pode precisar ser adicionado para evitar esses erros no futuro.

As classes .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 como enviar mensagens de email em um aplicativo ASP.NET marcar meu artigo, Enviando Email de um site de Páginas da Web do ASP.NET e as perguntas frequentes do System.Net.Mail.

Observação

O <system.net> elemento contém as configurações do servidor SMTP usadas pela SmtpClient classe ao enviar um email. Sua empresa de hospedagem na Web provavelmente tem um servidor SMTP que você pode usar para enviar emails de seu aplicativo. Consulte a seção de suporte do host da Web 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 Application_Error manipulador de 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 email enviado ao desenvolvedor. O código começa referenciando o HttpException retornado pelo GetLastError método (lastErrorWrapper). A exceção real que foi gerada pela solicitação é recuperada por meio lastErrorWrapper.InnerException de e é atribuída à variável lastError. As informações de rastreamento de tipo, mensagem e pilha são recuperadas e armazenadas em três variáveis de lastError cadeia de caracteres.

Em seguida, um MailMessage objeto chamado mm é criado. O corpo do email é 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 YSOD (Tela Amarela de Morte) dos Detalhes da Exceção 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 cuidado: se a exceção que disparou o Error evento for 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. 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 constantes e FromAddress de para qualquer endereço de support@example.com email para o qual o email de notificação de erro deve ser enviado e originado. Você também precisará especificar as configurações do servidor SMTP na <system.net> seção em Web.config. Consulte seu provedor de host 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 recebe uma mensagem de email que resume o erro e inclui o YSOD. No tutorial anterior, demonstramos um erro de runtime 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 de Morte dos 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 email.

A Figura 2 mostra o email recebido ao visitar Genre.aspx?ID=foo. O corpo do email resume as informações de 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 email enviado ao desenvolvedor.

Figura 2: o desenvolvedor recebe uma notificação de Email sempre que há uma exceção sem tratamento
(Clique para exibir a imagem em tamanho real)

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

Figura 3: a notificação de Email inclui os detalhes da exceção YSOD como um anexo
(Clique para exibir a imagem em tamanho real)

E o uso da página de erro personalizada?

Este tutorial mostrou como usar Global.asax e o Application_Error manipulador de eventos para executar o código quando ocorre uma exceção sem tratamento. Especificamente, usamos esse manipulador de eventos para notificar um desenvolvedor de um erro; podemos 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 de Detalhes do Erro, o Erro de Runtime YSOD 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 é mostrado na 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 para esse comportamento é porque a página de erro personalizada é acessada por meio de um redirecionamento. Quando uma exceção sem tratamento atinge o ASP.NET runtime, o mecanismo de 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 http 302 status, instruindo o navegador a solicitar uma nova URL, ou seja, a página de erro personalizada. Em seguida, 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 é alterada 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 exibir a imagem em tamanho real)

O efeito líquido é que a solicitação em que a exceção sem tratamento ocorreu termina quando o servidor responde com o redirecionamento HTTP 302. A solicitação subsequente para a página de erro personalizada é uma nova solicitação; nesse ponto, o mecanismo de 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 Global.asax pelo 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 manipulador de eventos transfere o Application_Error controle para a página de erro personalizada apropriada com base no código http status. Como o controle foi transferido, a página de erro personalizada tem acesso às informações de exceção sem tratamento por meio Server.GetLastError de e pode notificar um desenvolvedor sobre o erro e registrar seus detalhes. A Server.Transfer chamada impede que o mecanismo de 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 do 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. Há duas maneiras de criar um manipulador de eventos para HttpApplication eventos como Error: no Global.asax arquivo ou em um módulo HTTP. Este tutorial mostrou como criar um Error manipulador de eventos no arquivo que notifica os Global.asax desenvolvedores de um erro por meio de uma mensagem de email.

Criar um Error manipulador de eventos será ú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.

Programação feliz!

Leitura Adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos: