ASP .NET - Resolvendo o problema de estado da sessão
Neste artigo vamos abordar duas maneiras de resolver o problema de estado da sessão.
O que quer dizer problema de estado da sessão ?
Quando o usuário de um web site (geralmente comércio eletrônico) clica no botão do navegador para retornar para página ASP .NET anterior de um formulário e então posta o formulário , o estado da sessão da aplicação pode não corresponder ao do formulário.
O problema é mais fácil de entender em um cenário de carrinho de compras.
Para isso eu vou usar como exemplo o carrinho de compras do meu artigo: ASP .NET - Criando um carrinho de compras II (VB .NET)
Neste cenário o carrinho de compras de um usuário é armazenado na sessão (geralmente) exibindo os itens selecionados:
O cliente adicionou dois
itens ao seu carrinho que é armazenado na sessão, e contém os itens Ipad 16 e NoteBook LG conforme mostra a figura ao lado.
|
Quando o cliente, após realizar selecionar alguns itens, deleta um dos itens do carrinho, temos uma alteração dos dados na sessão.
O usuário removeu o item
NoteBook Lg do carrinho de compras. Temos agora armazenado na sessão o carrinho com um item. |
Se o cliente mudar de idéia e clicar no botão retornar do navegador, todos os itens serão exibidos novamente, mesmo que o estado da sessão contenha somente um item.
O cliente clicou no botão
retornar do navegador e a página voltou a exibir os dois
itens existentes no carrinho, mas na sessão o carrinho atual possui somente um item. |
Se neste momento o cliente encerrar as compras (checkout) o pedido irá mostrar apenas um item quando no formulário temos exibido um número diferente o que pode confundir o cliente. (Isso se quando ele clicar no botão do navegador para retornar não ocorrer um erro.)
Para tentar resolver este problema podemos adotar um dos seguintes procedimentos :
Vamos analisar cada uma das opções citadas.
1 - Desabilitar o cache de páginas do Navegador
O problema :
Quando o usuário clica no botão do navegador para retornar para a página anterior o navegador retorna a página anterior localizada no cache local do navegador sem notificar o servidor e como resultado as informações armazenadas no estado da sessão podem estar fora de sincronia com os dados exibidos pela página atual.
A solução :
Desabilitar o cache de páginas do navegador
Como fazer :
Para desabilitar o cache do navegador em aplicações ASP .NET podemos usar os seguintes métodos:
Método | Descrição |
Response.Cache.SetCacheability | Indica como a página deverá ser armazenada no cache. Para suprimir o cache devemos usar: HttpCacheability.NoCache |
Response.Cache.SetExpires | Especifica quando o cache da página expira. Usar Now().AddSeconds(-1) para marcar a página como já expirada. |
Response.Cache.SetNoStore | Define que o navegador não utilize o cache. |
Response.AppendHeader | Inclui um cabeçalho no objeto response HTTP. Especificando "Pragma" para a chave e "no-cache" para o valor desabilita o cache. |
Exemplo:
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Response.Cache.SetExpires(Now().AddSeconds(-1) )
Response.Cache.SetNoStore()
Response.AppendHeader("Pragma"
, "no-cache")
Todos esses métodos funcionam incluindo informação no cabeçalho HTTP que são enviados ao navegador com a página.
Infelizmente alguns navegadores ignoram esses cabeçalhos , de forma que esta técnica não garante que as páginas não serão armazenadas no cache para todos os navegadores.
Mesmo assim não é uma má idéia
adotar incluir o código do exemplo no evento Load
das páginas ASP .NET com informações importantes que não
deverão estar no cache.
2
- Usar
timestamps ou números aleatórios para monitorar as páginas
O problema :
Quando o usuário clica no botão do navegador para retornar para a página anterior o navegador retorna a página anterior localizada no cache local do navegador sem notificar o servidor e como resultado as informações armazenadas no estado da sessão podem estar fora de sincronia com os dados exibidos pela página atual.
A solução :
Usar timestamps ou números aleatórios para monitorar as páginas de forma a identificar quando a página não é a atual - O objetivo é monitorar as páginas dos formulários críticos da aplicação via código de forma a detectar quando o cliente tenta postar uma página que não é a página atual. Para fazer isso uma das maneiras é usar timestamps ou números aleatórios para rastrear a utilização das páginas;
Como fazer :
A técnica básica para implementar esta solução é armazenar um timestamp em dois locais distintos quando a página é postada: no View State e na Session State.
- Assim quando o usuário postar a
página pela segunda vez o evento Page_Load
chama uma função privada chamada EstaExpirada;
- Esta função retorna o timestamp do View State e o timestamp da Session comparando-os;
- Se eles forem idênticos a página é a página atual e a
função EstaExpirada retorna False;
- Se os valores forem diferentes isto indica que o cliente postou
uma página que estava no cache usando o botão retornar do
navegador;
- Neste caso a função EstaExpirada retorna True
e o evento Page_Load redireciona o cliente para
uma página chamada Expirada.aspx;
- A página Expirada.aspx exibe uma mensagem indicando
que a página esta desatualizada e não pode ser postada;
Obs: Antes de realizar a comparação dos timestamps no View State e na Session a função EstaExpirada verifica se ambos os itens existem. Se eles não existirem a função retorna False de forma que os timestamps atuais possam ser salvos tanto no View State e na Session.
Outro detalhe importante é que como a página precisa ser postada de volta ao servidor você não pode usar a propriedade PostBackUrl do controle Button para exibir outra página se você desejar primeiro verificar você desejar verificar que a página atual não expirou.
Exemplo de código: (Este código deverá estar na página que verifica o timestamp)
Partial Class Carrinho Inherits System.Web.UI.Page Private carrinho As ItemLista Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load if EstaExpirada() Then Response.Redirect("Expirada.aspx") Else Me.SalvarTimeStamps() End If carrinho = ItemLista.GetCarrinho If Not IsPostBack Then Me.ExibeCarrinho() End If End Sub Private Function EstaExpirada() As Boolean If Session("Carrinho_TimeStamp") Is Nothing Then Return False ElseIf ViewState("TimeStamp") Is Nothing Then Return False ElseIf ViewState("TimeStamp").ToString = Session("Carrinho_TimeStamp").ToString Then Return False Else Return True End If End Function Private Sub SalvarTimeStamps() Dim dtm As DateTime = DateTime.Now ViewState.Add("TimeStamp" , dtm) Session.Add("Carrinho_TimeStamp", dtm) End Sub End Class |
Existem outras técnicas para contornar o problema mas as duas abordadas são bem simples e de fácil implementação.
"Passará o céu e a terra, mas as minhas palavras jamais passarão." (Mateus 24:35)
Referências: