Exceções podem ser definidas, em termos gerais, como instâncias de classes empregadas na representação de erros que aconteceram ao longo da execução de uma aplicação. Trata-se de um mecanismo presente não apenas no .NET Framework, como também em outras plataformas de desenvolvimento como Java, por exemplo.

Tomando por base a plataforma .NET, todas as exceções manipuladas dentro desta derivam da classe básica Exception (namespace System). Este é o caso de tipos que definem erros bastante comuns em aplicações baseadas no framework .NET, tais como SqlException (exceção gerada ao ocorrerem erros no acesso a uma base de dados do SQL Server), DivideByZeroException (erro disparado ao se tentar efetuar uma divisão por zero), OutOfMemoryException (lançada quando não existir memória suficiente para a execução de uma operação) e SecurityException (este erro acontece quando um problema de segurança for detectado, como por exemplo a tentativa de acesso a um diretório para o qual se retiraram as permissões de leitura).

Além dos exemplos de exceções que foram citados, o .NET Framework disponibiliza nativamente um grande número de outros tipos que correspondem a erros, com cada uma dessas classes se destinando à representação de falhas em cenários gerais. No entanto, haverá situações nas quais desenvolvedores precisarão criar uma ou mais classes a serem empregadas para se lançarem erros bem específicos: a estas estruturas dá-se o nome de exceções customizadas.

A própria documentação da plataforma .NET estabelece algumas diretrizes a serem consideradas na implementação de exceções customizadas:

  • A classe-filha derivar, sempre que possível, do tipo Exception ou de outra classe básica;
  • Evitar hierarquias extensas, ou seja, criar vários níveis de exceções, em que um tipo herda do outro;
  • É extremamente aconselhável que o nome da exceção terminar com o sufixo Exception, de maneira a identificar mais facilmente a finalidade a que se presta a classe em questão.

Procurando demonstrar os conceitos até aqui mencionados, será criado um exemplo de exceção customizada, empregando-se o mesmo em uma aplicação ASP.NET detalhada nas próximas seções deste artigo.

Criando a solução de exemplo

A aplicação apresentada neste artigo foi criada no .NET framework 4.0, através da utilização do Microsoft Visual Studio 2010 Ultimate Edition (o conteúdo pode ser baixado a partir de um link para download que se encontra nesta página). O exemplo demonstrado a seguir procura abordar a implementação de uma exceção customizada, bem como o tratamento desta em um projeto ASP.NET Web Forms. O

site aqui detalhado contará com uma funcionalidade para o cálculo do IMC (Índice de Massa Corpórea) de uma pessoa.

Para gerar o projeto de testes será necessário, dentro do Visual Studio, acessar o menu File, opção New, sub opção Project. Dentro da tela New Project (Figura 1) selecionar o template ASP.NET Web Application, preenchendo o campo Name com o nome da aplicação a ser gerada (“TesteExceptions”, neste caso); no campo Location é possível ainda definir o diretório no qual serão criados os arquivos para este projeto.

Criando um projeto ASP.NET para testes
Figura 1: Criando um projeto ASP.NET para testes

Criação das classes para os testes com exceções customizadas

Com o projeto TesteExceptions criado, será necessário prosseguir com a geração das classes a serem utilizadas na implementação da aplicação de testes, bem como no tratamento de erros ocorridos nesta. Primeiramente será implementada a classe IMCException. Clique dentro do Solution Explorer com o botão direito do mouse sobre a Web Application TesteExceptions, escolhendo em seguida no menu de atalho a opção Add, sub opção New Item. Neste momento será exibida a tela Add New Item (Figura 2); preencher o campo Name com “IMCException.cs”.

Criando o arquivo para implementação da classe IMCException
Figura 2: Criando o arquivo para implementação da classe IMCException

A Listagem 1 apresenta a implementação da classe IMCException, a qual herda do tipo básico Exception. Essa estrutura corresponde a uma exceção que será disparada quando ocorrer uma falha em uma operação de cálculo do IMC de uma pessoa. Valores inválidos de altura e/ou peso ou, até mesmo, uma falha durante o cálculo (divisão por zero) serão alguns dos motivos que podem levar à geração de um erro através de uma instância de IMCException.

O construtor definido para o tipo IMCException recebe como parâmetro uma mensagem, a qual descreve o problema que ocasionou a exceção. Este construtor acessa ainda os elemento de mesmo tipo que foi definido na classe básica Exception. Isso acontece, em ambos os casos, através da invocação da palavra-chave base, a qual tem por função redirecionar a chamada para o construtor implementado na classe-pai. O aproveitamento dos construtor definido em Exception permite ao tipo IMCException evitar a implementação de características que já foram codificadas na estrutura-pai.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TesteExceptions
{
    public class IMCException : Exception
    {
        public IMCException(string mensagem)
            : base(mensagem)
        {
        }
    }
}
Listagem 1: Classe IMCException

O tipo IMC (Listagem 2) será empregado no cálculo do IMC de uma pessoa. Essa estrutura representa um exemplo de classe estática, ou seja, que não dependerá da geração de uma nova instância de objeto para a sua utilização de funcionalidades definidas na mesma.

Será a partir do método estático CalcularIMC que ocorrerá o processamento de informações de peso e altura de uma pessoa, obtendo-se a partir disto o Índice de Massa Corpórea (IMC) da mesma. Os dois parâmetros recebidos por esta operação (pesoPessoa e alturaPessoa) foram declarados como sendo do tipo object, o que significa que qualquer tipo de informação poderá ser fornecida a estes métodos (valores string ou, mesmo, já convertidos para um tipo numérico como double).

Inicialmente, CalcularIMC acessa a operação ConverterParametroIMC, a fim de transformar as medidas passadas como parâmetro em valores do tipo double, os quais serão associados às variáveis pesoConvertido e alturaConvertida.

Já no método estático ConverterParametroIMC, procede-se primeiramente com a conversão da medida que foi informada como parâmetro. Isto é feito através de uma chamada à operação ToDouble definida na classe Convert, com todo este processo acontecendo dentro de um bloco try-catch. Caso ocorram erros, o fluxo de execução é direcionado para a cláusula catch; uma nova instância da exceção IMCException é então criada, com o construtor recebendo como parâmetros uma mensagem que indica a ocorrência de uma falha na conversão (mensagemErroConversao).

Se o valor convertido no interior da operação ConverterParametroIMC corresponder a um número menor ou igual a zero, também será lançada uma exceção do tipo IMCException (a partir da mensagem associada ao parâmetro mensagemFaixaInvalidaValor).

A ocorrência de qualquer erro ao se chamar a operação ConverterParametroIMC fará com que o método CalcularIMC aborte a sua execução, sem continuar desse modo com o cálculo do IMC. A determinação deste valor também ocorre dentro de um bloco try-catch; na eventualidade de algum problema que aconteça durante o processamento da expressão para se chegar ao valor esperado, um erro também baseado em IMCException será disparado (novamente dentro de uma cláusula catch).


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TesteExceptions
{
    public static class IMC
    {
        private static double ConverterParametroIMC(
            object valor,
            string mensagemErroConversao,
            string mensagemFaixaInvalidaValor)
        {
            double valorConvertido;
            try
            {
                valorConvertido = Convert.ToDouble(valor);
            }
            catch
            {
                throw new IMCException(mensagemErroConversao);
            }

            if (valorConvertido <= 0)
            {
                throw new IMCException(mensagemFaixaInvalidaValor);
            }

            return valorConvertido;
        }

        public static double CalcularIMC(
            object pesoPessoa, object alturaPessoa)
        {
            double pesoConvertido = ConverterParametroIMC(
                pesoPessoa,
                "Erro durante a conversão do Peso.",
                "O Peso informado dever ser maior do que zero.");

            double alturaConvertida = ConverterParametroIMC(
                alturaPessoa,
                "Erro durante a conversão da Altura.",
                "A Altura informada dever ser maior do que zero.");

            try
            {
                return (pesoConvertido / 
                           (alturaConvertida * alturaConvertida));
            }
            catch
            {
                throw new IMCException(
                    "Erro durante o cálculo do IMC.");
            }
        }
    }
}
Listagem 2: Classe IMC

Implementação do site para testes

Para a implementação da Web Application TesteExceptions foi utilizado o template padrão que é disponibilizado pelo Visual Studio. Em virtude disto, algumas mudanças nas páginas que foram criadas automaticamente estarão sendo feitas conforme especificado a seguir, a fim de tornar mais simples a aparência e a estrutura da aplicação de exemplo.

Na Listagem 3 encontra-se o código que define a aparência da Master Page Site.Master, a qual é referenciada pelas demais páginas do site. Esse tipo de estrutura conta normalmente com elementos visuais (menus, abas, legendas etc.) comuns às várias páginas de uma aplicação. Assim, a utilização deste recurso evita a replicação de controles e outros itens ao longo de uma solução Web (algo que dificultaria atividades de manutenções futuras).

Já a Listagem 4, apresenta o código que determina como a página Sobre (About.aspx) será visualizada via browser. O atributo MasterPageFile definido dentro da declaração inicial da página especifica que a mesma faz uso da Master Page Site.Master.


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="TesteExceptions.SiteMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head runat="server">
    <title></title>
    <link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
    <asp:ContentPlaceHolder ID="HeadContent" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form runat="server">
    <div class="page">
        <div class="header">
            <div class="title">
                <h1>
                    Teste do uso de Exceptions em Aplicações ASP.NET
                </h1>
            </div>
            <div class="clear hideSkiplink">
                <asp:Menu ID="NavigationMenu" runat="server" 
                    CssClass="menu" EnableViewState="false"
                    IncludeStyleBlock="false" 
                    Orientation="Horizontal">
                    <Items>
                        <asp:MenuItem NavigateUrl="~/Default.aspx" 
                            Text="Home" />
                        <asp:MenuItem NavigateUrl="~/About.aspx" 
                            Text="Sobre" />
                    </Items>
                </asp:Menu>
            </div>
        </div>
        <div class="main">
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
        </div>
        <div class="clear">
        </div>
    </div>
    <div class="footer">
    </div>
    </form>
</body>
</html>
Listagem 3: Código da Master Page Site.Master

<%@ Page Title="Sobre" Language="C#" MasterPageFile="~/Site.master" 
    AutoEventWireup="true"
    CodeBehind="About.aspx.cs" Inherits="TesteExceptions.About" %>
<asp:Content ID="HeaderContent" runat="server" 
     ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" 
     ContentPlaceHolderID="MainContent">
    <h2>
        Sobre
    </h2>
    <p>
        Esta é uma aplicação para teste do uso de
        Exceptions em ASP.NET.
    </p>
</asp:Content>
Listagem 4: Código da página About.aspx

Será necessário agora prosseguir com a implementação da funcionalidade para cálculo do IMC. Isto acontecerá dentro da página Default.aspx, a qual foi gerada de maneira automática ao se criar o projeto TesteExceptions.

Adicionar ao formulário CalculoIMC.aspx dois controles TextBox (campos de peso e altura), um Button (o valor da propriedade Text deste componente será “Calcular IMC”) e, por fim, dois do tipo Label (conforme indicado na Tabela 1). Estes componentes serão utilizados no cálculo do IMC de uma pessoa, determinando ainda se está última encontra-se dentro da faixa de peso normal ou não.

Tipo do Controle Nome do Controle
TextBox txtPeso
TextBox txtAltura
Button btnCalcularIMC
Label lblValorIMC
Label lblSituacaoPessoa
Tabela 1: Controles a serem adicionados à página CalculoIMC.aspx

A Figura 3 corresponde, em termos gerais, a um exemplo de como poderá ficar a página Default.aspx ao término dos procedimentos mencionados nesta seção. Esta imagem foi gerada a partir da execução da aplicação e do acesso a esta última via Internet Explorer.

Página Default.aspx em execução, com todos os controles já configurados
Figura 3: Página Default.aspx em execução, com todos os controles já configurados

Ao clicar no botão btnCalcularIMC será realizado o cálculo do IMC. O evento que corresponde a esta ação é demonstrado na Listagem 5. O método btnCalcularIMC_Click faz uso da classe IMC, invocando a operação CalcularIMC (que recebe como parâmetros as medidas digitadas nos campos disponíveis em tela) e exibindo o valor correspondente (variável valorIMC) e a situação do peso da pessoa em tela.


...

protected void btnCalcularIMC_Click(object sender, EventArgs e)
{
    double valorIMC = IMC.CalcularIMC(txtPeso.Text, txtAltura.Text);
            
    lblValorIMC.Text = valorIMC.ToString();

    if (valorIMC < 18.5)
        lblSituacaoPessoa.Text = "Abaixo do Peso";
    else if (valorIMC > 25)
        lblSituacaoPessoa.Text = "Acima do Peso";
    else
        lblSituacaoPessoa.Text = "Peso Normal";
}

...
Listagem 5: Evento btnCalcularIMC_Click

Caso ocorram erros durante o processo de cálculo do IMC, será apresentada ao usuário uma tela similar à que consta na Figura 4. Embora para efeitos de desenvolvimento as informações exibidas no browser sejam úteis, não é recomendável se exibir isto a um usuário convencional: uma boa prática neste caso, seria a criação de uma página mais amigável indicando a ocorrência de um erro. Além disso, pessoas com um maior conhecimento podem, a partir das informações presentes numa tela de erro default do ASP.NET, explorar prováveis vulnerabilidades do sistema considerado.

Página de erro default do ASP.NET
Figura 4: Página de erro default do ASP.NET

O ASP.NET permite que uma página customizada para exibição de erros seja criada, com o formulário correspondente sendo exibido sempre que uma exceção não for tratada dentro de uma página da aplicação. Este é um recurso que permite centralizar a exibição de mensagens de erro, customizando as informações que são apresentadas aos usuários do site em questão.

Para a criação da página que centraliza a apresentação de erros será criado o formulário Erro.aspx. A geração dos arquivos correspondentes a esta Web Page é feita clicando-se com o botão direito do mouse sobre a Web Application TesteExceptions e selecionando no menu de atalho a opção Add, subopção New Item. Neste momento será exibida a tela Add New Item (Figura 5); marcar o template “Web Form using Master Page” e preencher o campo Name com “TesteSessionViewState.aspx”.

Clicando no botão “Add” será solicitado ao usuário que defina qual a Master Page para o Web Form que está sendo montado (Figura 6). A única opção possível no caso é “Site.Master”; finalizar então este processo acionando a opção “OK”.

Criando os arquivos para implementação da página Erro.aspx
Figura 5: Criando os arquivos para implementação da página Erro.aspx
Selecionando a Master Page da página Erro.aspx
Figura 6: Selecionando a Master Page da página Erro.aspx

Adicionar à página Erro.aspx um controle Label de nome “lblErro”. Este componente exibirá a mensagem correspondente ao último erro disparado pela ação corrente do usuário. A Listagem 6 apresenta a implementação do formulário Erro.aspx. Dentro do evento Page_Load é invocado o método GetLastError do objeto Server, atribuindo o valor correspondente à variável ultimoErro; caso exista uma InnerException, opta-se então por preencher ultimoErro com o valor presente nesta propriedade: aplicações ASP.NET costumam gerar novas exceções para erros não tratados, atribuindo o objeto que representa a falha original à propriedade InnerException.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace TesteExceptions
{
    public partial class Erro : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                Exception ultimoErro = Server.GetLastError();
                if (ultimoErro.InnerException != null)
                    ultimoErro = ultimoErro.InnerException;
                lblErro.Text = ultimoErro.Message;
            }
        }
    }
}
Listagem 6: Implementação da página Erro.aspx

Um último ajuste precisará ser feito no arquivo Web.config da aplicação. A definição de uma página customizada de erros exige que uma configuração correspondente seja especificada. A Listagem 7 demonstra isto. O elemento customErrors é a base para a utilização deste tipo de técnica:

  • O atributo mode preenchido como “On” indica que a exibição da página customizada de erros foi ativada (para desativar especificar “Off”);
  • Já o atributo defaultRedirect indica o endereço da página de erro;
  • Por fim, o atributo redirectMode preenchido como ResponseWrite determina que a URL original na qual o erro aconteceu é mantida na apresentação da página de erro customizada.

<?xml version="1.0"?>
<configuration>
  <system.web>

   ...

    <customErrors
         mode="On"
         defaultRedirect="~/Erro.aspx"
         redirectMode="ResponseRewrite">
    </customErrors>

   ...
  </system.web>

  ...

</configuration>
Listagem 7: Configuração de uma página customizada de erros no Web.config

Conclusão

A grande vantagem das exceções customizadas está na possibilidade de geração de informações mais claras sobre erros. Falhas genéricas que aconteçam dentro de um determinado contexto podem então ser capturadas, repassando-se como erro objetos que são instâncias de classes específicas correspondentes. Espero que o conteúdo aqui exposta possa auxiliá-lo no dia-a-dia. Até uma próxima oportunidade!