Descobrindo quando o usuário sai de uma aplicação ASP.NET – Parte 2

Introdução

 

Na primeira parte deste artigo, foi mostrado como podemos detectar a inatividade do usu√°rio de uma aplica√ß√£o ASP.NET atrav√©s do evento Session_End. Como explicado, o evento Session_End ocorre somente quando o tempo de inatividade especificado √© atingido. Ou seja, quando o usu√°rio acessa um outro site ou fecha o browser, o evento Session_End n√£o √© disparado automaticamente. Isso acontece porque os eventos de troca de site e de fechamento de browser ocorrem no lado cliente da aplica√ß√£o, enquanto que o evento Session_End √© um evento que ocorre no lado servidor. Outra limita√ß√£o do evento Session_End √© que ele s√≥ √© chamado quando o modo do estado de sess√£o estiver configurado como InProc. Ou seja, quando utilizarmos algum servidor de estado para armazenar a sess√£o, o evento n√£o ser√° disparado. Caso desejemos fazer algum processamento no exato momento em que o usu√°rio acessa outro site ou fecha o browser, precisamos desenvolver alguma funcionalidade que ‚Äúavise‚ÄĚ o servidor que algum desses eventos ocorreu. Nesta parte do artigo, explicarei algumas t√©cnicas para que a aplica√ß√£o possa notificar a ocorr√™ncia desses eventos ao servidor.

 

 

Evento onunload

 

Quando trocamos o site que estamos visitando ou fechamos a janela do browser, o evento javascript onunload é disparado no lado cliente. Portanto, esse é o local ideal para colocarmos o código de tratamento específico para quando o usuário deixar a aplicação. O que se costuma fazer neste evento é chamar uma página que seja responsável pelo encerramento da sessão do usuário. Isso pode ser feito de algumas maneiras. A forma mais comum era utilizar código javascript para abrir uma popup, apontando para a página responsável pela finalização da sessão. Com a crescente popularização dos bloqueadores de popups, essa técnica tornou-se ineficiente, pois os bloqueadores, ao impedirem a abertura da popup, impossibilitam que a página no servidor seja chamada. No exemplo deste artigo, será utilizado o conceito de AJAX - Asynchronous JavaScript and XML, para fazer a chamada à página. Explicada de maneira simplificada, a técnica AJAX permite que código javascript acesse o servidor web sem a necessidade de submeter a página, como ocorreria com um Web Form tradicional. Isso permitirá que o servidor seja acessado sem a necessidade de abertura de popup.

 

No Visual Studio 2005, abra o Web Site Saida que foi criado na primeira parte deste artigo. No Solution Explorer, clique com o botão direito do mouse sobre o Web Site Saida e escolha a opção Add New Item. Na janela que é aberta, escolha o template Web Form. Dê o nome de Finaliza.aspx e escolha a linguagem Visual C#. Esta página que acabamos de criar será a responsável por expirar a sessão do usuário quando o evento onunload ocorrer. Ainda no Solution Explorer, clique com o botão direito sobre a página Finaliza.aspx e escolha a opção View Code, para que o editor de código do Visual Studio seja aberto. No evento Page_Load da página, adicione o seguinte código:

 

 

protected void Page_Load(object sender, EventArgs e)

{

    Session.Abandon();

}

 

 

No código acima, chamamos o método Abandon() do objeto Session, que é responsável por finalizar a sessão atual. Quando ele é executado, forçamos a expiração da sessão. Assim, o evento Session_End também é disparado quando o método Abandon() é executado.

 

Obs: lembre-se que se estiv√©ssemos utilizando um modo de armazenamento de sess√£o diferente de InProc, o evento Session_End n√£o seria chamado. Neste caso, ter√≠amos que colocar o c√≥digo do evento Session_End dentro do evento Page_Load da p√°gina Finaliza.aspx. Para mais informa√ß√Ķes sobre as formas de armazenamento de sess√£o do ASP.NET, consulte as Refer√™ncias ao final do artigo.

 

Agora, no evento onunload do browser, precisamos programar a chamada √† p√°gina Finaliza.aspx. No Solution Explorer, clique com o bot√£o direito do mouse sobre a p√°gina Pagina1.aspx e escolha a op√ß√£o View Markup. Isso nos levar√° ao conte√ļdo HTML da p√°gina. Na tag <body>, adicione o atributo onunload com a chamada para a fun√ß√£o javascript que ser√° executada quando este evento ocorrer. A tag dever√° ficar parecida com o seguinte trecho:

 

 

<body onunload="finaliza();">

 

 

Dentro das tags <head></head>, declare a fun√ß√£o javascript finaliza(), conforme o c√≥digo abaixo. Nele, utilizamos o objeto XMLHttpRequest, que √© o principal componente do AJAX e respons√°vel pelo conex√£o com o servidor Web. Como existem algumas diferen√ßas na forma em que este objeto √© instanciado entre cada browser, fazemos v√°rias tentativas at√© que tenhamos um objeto v√°lido. Depois, chamamos o m√©todo open() passando como par√Ęmetros o m√©todo de acesso √† p√°gina (‚ÄúGET‚ÄĚ), a p√°gina que desejamos acessar (‚ÄúFinaliza.aspx‚ÄĚ) e um valor booleano indicando se a requisi√ß√£o √© ass√≠ncrona. Quando passamos true neste par√Ęmetro, a chamada √© feita assincronamente. Isso significa que o processamento do script continuar√°, independentemente da resposta vir do servidor. Assim, o browser n√£o bloqueia o acesso a p√°gina e o usu√°rio continua trabalhando com ela normalmente. Se pass√°ssemos false, a chamada seria feita em modo s√≠ncrono, ou seja, o browser iria esperar a resposta do servidor web para dar continuidade a execu√ß√£o do script. Enquanto a resposta n√£o viesse, o usu√°rio n√£o conseguiria fazer mais nada na p√°gina, dando a sensa√ß√£o de travamento. Por √ļltimo, chamamos o m√©todo send() para que o acesso seja feito.

 

 

<script language="javascript" type="text/javascript">

function finaliza()

{

    var xmlHttp=false;

 

    try {

      xmlHttp = new XMLHttpRequest();

    } catch (e1) {

      try {

        xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");

      } catch (e2) {

          xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");

      }

    }

   

    xmlHttp.open("GET", "Finaliza.aspx", true);

    xmlHttp.send(null);

}   

</script>   

 

 

 

No Solution Explorer, clique com o bot√£o direito do mouse sobre a p√°gina Pagina1.aspx e escolha a op√ß√£o View in Browser. Para testar o funcionamento do que acabamos de fazer, acesse um outro site ou ent√£o feche o browser. Ap√≥s uma dessas a√ß√Ķes, v√° ao Control Panel do Windows ¬ģ Administrative Tools ¬ģ Event Viewer. Selecione o log Application e veja o log que foi gerado por nossa aplica√ß√£o. Isso mostra que quando o evento onunload foi disparado por alguma a√ß√£o do lado cliente, a fun√ß√£o javascript finaliza() chamou a p√°gina que desenvolvemos para for√ßar a expira√ß√£o da sess√£o, o que por sua vez gerou a chamada ao evento Session_End, que gravou a informa√ß√£o de sess√£o expirada no log de evento do Windows.

 

Conclus√£o

 

Na segunda parte do artigo vimos como é possível acionar uma página no servidor web a partir de eventos ocorridos no cliente (browser). Assim, podemos detectar quando o usuário acessa um outro site ou então quando fecha o browser. Também solucionamos o problema causado pela limitação do Session_End só ser chamado quando utilizamos o modo de armazenamento InProc. Entretanto, ainda temos um problema nessa solução. Do jeito que foi implementada, sempre que outra página for acessada, a página Finaliza.aspx será chamada. O evento onunload não faz distinção entre páginas da mesma aplicação ou páginas de outros sites. Esse evento sempre é chamado em resposta à saída da página atual. Isso é um problema, já que uma aplicação web normalmente é composta de várias páginas. Assim, ao acessarmos uma outra página de nossa aplicação, a sessão acabará sendo finalizada, o que não é o comportamento correto. Na próxima parte deste artigo, explicarei como podemos contornar este problema e somente finalizar a sessão quando a aplicação for abandonada, e não a página.

 

 

Referências

 

Código fonte do exemplo deste artigo

http://www.aspneti.com/artigos/ExemploDetectarSaida.zip

 

Gerenciamento de Estado no Servidor ‚Äď Parte 1 http://www.msdnbrasil.com.br/sharepedia/visualizarartigo.aspx?id=57763

 

Gerenciamento de Estado no Servidor ‚Äď Parte 2

http://www.msdnbrasil.com.br/sharepedia/visualizarartigo.aspx?id=57906

 

 

Dynamic HTML and XML: The XMLHttpRequest Object

http://developer.apple.com/internet/webcontent/xmlhttpreq.html

 

 

 

Sobre o Autor

 


Ricardo Oneda Pereira de Toledo √© formado em Processamento de Dados pela Faculdade de Tecnologia de S√£o Paulo, √© MCAD ‚Äď Microsoft Certified Application Developer, com √™nfase em aplica√ß√Ķes Web com C# e Banco de Dados SQL Server 2000 e √© MVP ‚Äď Most Valuable Professional em ASP/ASP.NET. Atualmente trabalha como Analista de Sistemas no desenvolvimento de aplica√ß√Ķes Web. Visite o blog em http://oneda.mvps.org/blog