1. Gerenciamento de estado: Como manter informações entre requisições no Asp.Net

Diferentemente das aplicações Windows, aplicações web não mantem o estado da conexão, ou seja, nenhuma informação gerada no processo de comunicação é armazenada. Toda vez que for realizada uma requisição, o servidor não saberá de quem é a solicitação e nem se esse usuário já visitou o site antes. Além do fato de que após cada processamento e envio de páginas, todos os recursos alocados e utilizados são destruídos para preservação da memória do servidor. Este é um procedimento importante, pois em um ambiente web, o servidor deve atender várias solicitações simultâneas, e para isso, deve possuir recursos suficientes, recursos esses que são recuperados com a destruição dos objetos utilizados.

Neste cenário não é possível, por exemplo, armazenar o nome de um usuário e utilizá-lo em outras páginas, pois a aplicação não poderá manter a informação. Para contornar esse problema, o Asp.Net disponibiliza alguns mecanismos para o armazenamento e gerenciamento do estado da aplicação, sendo possível implementar em aplicações web, um comportamento semelhante ao gerenciamento de estado das aplicações Windows.

2. Gerenciamento de estado no cliente x servidor

O Asp.Net utiliza dois tipos de gerenciamento de estado: gerenciamento no cliente e no servidor. Cada tipo possui procedimentos diferentes para trabalhar com a informação que deve ser armazenada.

No modo cliente, todas as informações são armazenadas no computador que gerou a requisição, assim, a cada solicitação, todas as informações devem ser enviadas ao servidor, e após processadas, reenvidas para o armazenamento no cliente. No segundo modo, todas as informações são mantidas no servidor, apenas um código é transmitido na comunicação para identificar o cliente.

Adiante seguem imagens ilustrando o procedimento utilizado para cada modo de gerenciamento:

Gerenciamento do estado da aplicação no cliente

Figura 1: Gerenciamento do estado da aplicação no cliente

Gerenciamento do estado da aplicação no servidor

Figura 2: Gerenciamento do estado da aplicação no servidor

Alguns requisitos devem ser levados em conta na hora de decidir qual tipo de gerenciamento se adequa melhor para cada situação. Cada tipo possui seus prós e contras, e a escolha de um ou outro irá depender do que é mais crítico para sua aplicação.

Se escalabilidade é um fator importante, onde a aplicação deve atender o máximo de requisições simultâneas possíveis, deve-se escolher o gerenciamento no lado do cliente. O armazenamento das informações no cliente evita a sobrecarga da memória do servidor, pois este não precisa guardar as informações de cada usuário que solicitar uma página, deixando assim, mais espaço em memória para atender outras solicitações.

Outro ponto que favorece o lado do cliente é a possibilidade de se utilizar diferentes web services. Com o gerenciamento no servidor, de modo padrão, não é possível a utilização direta de múltiplos servidores, pois a informação está armazenada em um único ponto, e se no meio de uma transação for necessária a troca, este novo servidor não terá nenhuma informação a respeito do usuário.

Se a segurança é um ponto crítico na aplicação, o armazenamento no servidor é mais recomendado por ser mais seguro. As informações não serão transmitidas através da rede, e não estarão disponíveis para fácil manipulação na máquina do cliente. Assim, deve-se evitar guardar dados sensíveis no cliente, como senhas, perfis de acesso e demais dados sigilosos, essas informações estarão mais seguras dentro do servidor.

No modo cliente as informações são transmitidas e retransmitidas a cada requisição, com isso uma largura de banda maior é necessária para realização do processo de comunicação. Se a aplicação precisar armazenar muita informação é aconselhável a utilização do servidor, pois a transmissão desses dados pode onerar a comunicação tornando o carregamento de páginas mais lento, aumentando a largura de banda necessária para execução da aplicação.

A escolha de um método não exclui a possibilidade da utilização do outro, seu sistema muito provavelmente utilizará uma mistura dos dois tipos. Para cada funcionalidade deve-se escolher o método que melhor atende os requisitos de execução, atentando para o que é crítico para sua aplicação.

3. Gerenciamento no cliente

Conforme exposto no item anterior, o gerenciamento de estado no cliente é utilizado quando se deseja economizar recursos do servidor, aumentando assim sua escalabilidade e disponibilidade. Para realizar este tipo de gerenciamento o Asp.Net disponibiliza quatro mecanismo de armazenamento, a View state, os Hidden fields, os Cookies e as Query strings. As seções seguintes abordam cada um em detalhes:

3.1. View state

A View state é um recurso nativo do Asp.Net, utilizado pelos controles para armazenar suas propriedade entre postbacks, ou seja, somente utilizada para guardar dados entre requisições de uma mesma página.

Armazenada em um Hidden Field, denominado “__VIEWSTATE”, seu conteúdo é codificado em uma string de base 64 e, em cada requisição, é enviada ao servidor para ser consultada sobre a configuração atual das propriedades da página e dos controles existentes. Abaixo um exemplo de como pode ser observada em uma página padrão.

Listagem 1: Exemplo de Hidden Field


<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"  value="/GuvXiuykijhk...=">

Para ter acesso a seu conteúdo, basta utilizar a propriedade da página “Page.ViewState”, que corresponde a um dicionário de objetos do tipo StateBag. Sua utilização é bastante simples, onde cada objeto possui um identificador único para acesso ao valor armazenado.

Listagem 2: Utilizando a propriedade ViewState


ViewState["Cor"] = Color.Black;

if (ViewState["Cor"] == null)
Cor = (Color)ViewState["Cor"];

O exemplo acima configura a variável “Cor” dentro da ViewState. Aqui dois pontos importantes são demonstrados, o primeiro é a necessidade de verificar a existência de valor dentro do dicionário, pois caso aconteça o acesso a uma variável nula, o sistema gerará uma exceção NullReferenceException. Outro ponto é o casting necessário toda vez que for utilizar um valor armazenado, isto acontece porque a ViewState converte todos os dados para o tipo “Object”, este procedimento permite o armazenamento de qualquer tipo de dados que possa ser serializado.

3.1.1. Segurança

Apesar de seu conteúdo ser codificado, a ViewState pode facilmente ser violada, pois consiste apenas em um campo oculto na página exibida ao cliente. Como um paliativo o Asp.Net possui o mecanismo de MAC (message authentication code), que consiste em um código de verificação criptografado adicionado ao conteúdo da ViewState, para verificar se a mesma foi violada, se positivo, o servidor irá rejeitar a requisição.

O mecanismo de MAC já vem habilitado por padrão, mas se for necessário é possível desativá-lo através da propriedade “EnableViewStateMac”, configurando-a para “false” nas diretivas da página. Também é possível desabilitá-lo para toda aplicação configurando a mesma propriedade no arquivo web.config raiz. Abaixo um exemplo das duas formas de desabilitar o MAC.

Em nível de página:

Listagem 3: Habilitando o MAC na página


    <%@ Page Language="C#" ... EnableViewStateMac="true" %>

Em nível de aplicação:

Listagem 4: Habilitando o MAC no web.config


    <system.web>
        <pages enableViewStateMac="true"></pages>        
    </system.web>

Mesmo com a codificação de seu conteúdo em uma string de base 64, é possível através de codificação reversa, ler uma ViewState. Se apenas o MAC não é suficiente para atender os critérios de segurança de sua solução, pois não se quer que usuários tenham acesso ao conteúdo armazenado, é possível ainda criptografar esses valores.

Para ativar a criptografia basta configurar a propriedade “ViewStateEncryptionMode” com um dos seguintes valores:

  • Always: O Asp.Net sempre irá criptografar o conteúdo da ViewState;
  • Never: A ViewState nunca será criptografada;
  • Auto: O Asp.Net não irá criptografar a ViewState, ao menos que um controle específico solicite a configuração através da chamada do método da página “RegisterRequiresViewStateEncryption()”.

Para a codificação e criptografia, o Asp.Net utiliza uma chave específica do servidor, machine.key, armazenada no arquivo machine.config.

Deve-se verificar a real necessidade de utilizar a criptografia a invés de guardar os dados no servidor, pois esse processo gera uma etapa a mais de processamento na geração da página.

3.1.2. Desabilitar ViewState

Pelo fato de ser um método de gerenciamento de estado no cliente, a ViewState deve ser enviada e reenviada a cada requisição. Deste modo, certa atenção deve ser dada ao seu tamanho, pois quanto maior, mais largura de banda será necessária para efetuar a comunicação com o servidor web. Seu tamanho está diretamente associado com a quantidade de controles existentes na página, quanto mais controles, maior será seu tamanho.

Em muitos casos não é necessária sua utilização, como no caso de controles de conteúdo estático, conteúdos que são alterados a cada postbacks, ou ainda controles que só mudam seus valores mediante a uma ação do usuário, como o texto em um textbox ou um item de lista selecionado.

Como solução para este problema é possível desabilitar o mecanismo de ViewState, através da propriedade “EnableViewState”, que pode ser tanto configurada em nível de página, ou em nível de aplicação, no arquivo web.config.

Em nível de página:

Listagem 5: Desabilitando ViewState na página


 <%@ Page Language="C#" ... EnableViewState="false" %>

Listagem 6: Desabilitando ViewState no web.config

<system.web>
        <pages  enableViewState="true"> </pages>        
</system.web>

Também é possível realizar uma configuração mais refinada em nível de controle através da propriedade “ViewStateMode”. Com esta propriedade e possível habilitar a ViewState apenas nos controles que são realmente necessários. Para isto basta ativar a propriedade EnableViewState para página, e nos controles desabilitar ou habilitar a propriedade ViewstateMode.

3.2. Hidden Fields

Os Hidden Fields são campos de armazenamento de dados ocultos para os usuários. Seus valores não são exibidos na página HTML, mas podem facilmente serem observados e até alterados via código fonte da página.

O ASP.Net conta com o objeto HiddenField para representá-los, abaixo um exemplo de sua utilização:

Listagem 7: HiddenField do ASP>net


<asp:HiddenField ID="hfHoraAtual" runat="server" Value="valor" />

Assim como a ViewState, eles são utilizados apenas para o armazenamento de informações de uma única página, mas ao contrário dela, não possuem sistemas de compressão, criptografia, hashing ou MAC, logo, se tornam bem mais fáceis de serem observados e alterados.

Outro ponto importante, é que seu conteúdo só pode ser enviado para o servidor utilizando o método HTTP Post, onde suas informações serão enviadas junto com as do form ao qual está associado.

3.3. Cookies

Os Cookies são pequenas porções de dados enviadas ao cliente com o objetivo de realizar o gerenciamento de estado, guardando informações personalizadas que possam identificar o usuário e suas preferências. Possuem um tempo de vida maior que os outros mecanismos de gerenciamento de estado no cliente, podem persistir mesmo quando o usuário fechar o browser ou desliga o computador.

Consistem em pequenos arquivos de texto que são armazenados no computador cliente, e a cada requisição são enviados ao servidor para que possa realizar algum processamento. Por ser um mecanismo de armazenamento de estado no cliente, possui os mesmos pontos de atenção dos demais, ou seja, podem ser facilmente visualizados e alterados pelos clientes da aplicação.

Para se criar um Cookie basta utilizar o método Add da coleção Cookies do objeto Response, passando para o método um objeto do tipo HttpCookie. Abaixo um exemplo:

Listagem 8: Adicionando cookies


Response.Cookies.Add(new HttpCookie("Mensagem", "Hello World"));

O Cookie criado será enviado para o cliente através do cabeçalho HTTP.

A leitura de seu conteúdo é semelhante a leitura da ViewState, através do acesso a uma coleção de chave e valor. Mas ao invés de acessar via objeto Response, deve-se o objeto Request, pois ele está sendo devolvido pelo browser do cliente.

Listagem 9: Recuperando o valor de um cookie


Request.Cookies["Mensagem"].Value;

3.3.1. Definido tempo de vida de um Cookie

Através da propriedade Expires é possível definir o tempo vida de um Cookie, passando para propriedade um objeto DateTime, informado a data em que ele deixará de ser válido. O exemplo a seguir configura a validade do Cookie para mais um dia.

Listagem 10: Definindo o tempo de validade de um cookie


Response.Cookies["Mensagem"].Expires = DateTime.Now.AddDays(1);

A exclusão é realizada configurando a propriedade Expires para uma data anterior a atual. Não é possível realizar a exclusão direta, pois o arquivo encontra-se na máquina do cliente, que por motivos de segurança não permite o acesso.

3.3.2. Definindo escopo

Por questões de segurança, não é possível definir Cookies para diferentes domínios, mas é possível definir seu escopo para que possa ser enviado apenas para um diretório específico de uma aplicação ou para todos os sites do domínio em que está hospedado.

Para realizar esta configuração basta informar o caminho em que o Cookie deverá ser enviado para a propriedade Path. O exemplo a seguir configura a propriedade para que apenas sejam enviado para páginas dentro do diretório “MinhaAplicacao”.

Listagem 11: Definição do path do cookie


Response.Cookies["Mensagem"].Path = "/MinhaAplicacao";

3.3.3. Atribuição de múltiplos valores

É possível inserir mais de um valor por Cookie, para isto basta utilizar uma chave de identificação para cada valor, interessante quando se quer agrupar informações relacionadas.

Listagem 12: Definindo múltiplos valores para o cookie


Response.Cookies["Mensagem"]["Manha"] = "Bom dia";
Response.Cookies["Mensagem"]["Tarde"] = "Boa tarde";
Response.Cookies["Mensagem"]["Noite"] = "Boa noite";

Quando se utiliza múltiplos valores só é possível configurar as propriedades Expires e Path para todos de uma vez, não é possível especificar para cada valor uma propriedade.

3.4. Query Strings

Query Strings representam expressões formadas por parâmetros que são anexadas a URL para transferir informações entre páginas. A Query String é colocada no final da URL e seu início é marcado pelo caractere “?”, após ele segue uma série de parâmetros e valores separados pelo caractere “&” que são transmitidos entre as páginas. Abaixo um exemplo:

Listagem 13: Exemplo de URL com Query String


http://www.minhaaplicacao.com.br/Default.aspx?Id=1&Cor=Azul&Nome=Maria

O exemplo acima irá passar para a página Default.aspx os parâmetros Id, Cor e Nome, com os respectivos valores 1, Azul e Maria.

Para se ter acesso aos parâmetros passados pela Query String basta acessar a propriedade Request.QueryString, informando o nome do parâmetro a que deseja ter acesso. O exemplo abaixo mostra como realizar o acesso ao parâmetro Id passado via Query String. Note a utilização do método HtmlEncode do objeto Server, este método evita que caso seja passada alguma tag HTML sua página seja desconfigurada.

Listagem 14: Usando HtmlEncode


Server.HtmlEncode(Request.QueryString["Id"]);

Um ponto de atenção importante é que os parâmetros das Query Strings só são passados através do método HTTP GET. Para se utilizar uma Query String basta disponibilizar um link em sua página contendo os parâmetros que deseja transferir.

4. Referências

  • NORTHRUP, Tony; SNELL, Mike.; MCTS Self-Paced Training Kit (Exam 70-515) - Web Applications Development with Microsoft .NET Framework 4.
  • MACDONALD, Matthew; FREEMAN, Adam; SZPUSZTA Mario. Pro Asp.Net 4 in C# 2010. 4. Ed.