Mantendo a sessão ASP.NET aberta / ativa

115

Qual é a maneira mais fácil e não obstrutiva de manter uma sessão ASP.NET ativa enquanto o usuário estiver com a janela do navegador aberta? São chamadas AJAX cronometradas? Eu quero evitar o seguinte: às vezes os usuários mantêm a janela aberta por um longo tempo, depois inserem coisas e, ao enviar, nada funciona mais porque a sessão do lado do servidor expirou. Não quero aumentar o valor do tempo limite por mais de 10 minutos no servidor, pois quero que as sessões fechadas (fechando a janela do navegador) atinjam o tempo limite rapidamente.

Sugestões, amostras de código?

Alex
fonte
Você pode verificar este link para obter a resposta também. Dotnetcurry.com/ShowArticle.aspx?ID=453
Desenvolvedor de

Respostas:

170

Eu uso JQuery para realizar uma chamada AJAX simples para um manipulador HTTP fictício que não faz nada além de manter minha sessão viva:

function setHeartbeat() {
    setTimeout("heartbeat()", 5*60*1000); // every 5 min
}

function heartbeat() {
    $.get(
        "/SessionHeartbeat.ashx",
        null,
        function(data) {
            //$("#heartbeat").show().fadeOut(1000); // just a little "red flash" in the corner :)
            setHeartbeat();
        },
        "json"
    );
}

O manipulador de sessão pode ser tão simples quanto:

public class SessionHeartbeatHttpHandler : IHttpHandler, IRequiresSessionState
{
    public bool IsReusable { get { return false; } }

    public void ProcessRequest(HttpContext context)
    {
        context.Session["Heartbeat"] = DateTime.Now;
    }
}

A chave é adicionar IRequiresSessionState, caso contrário, a Sessão não estará disponível (= null). O manipulador pode, é claro, também retornar um objeto serializado JSON se alguns dados devem ser retornados ao JavaScript de chamada.

Disponibilizado através do web.config:

<httpHandlers>
    <add verb="GET,HEAD" path="SessionHeartbeat.ashx" validate="false" type="SessionHeartbeatHttpHandler"/>
</httpHandlers>

adicionado de balexandre em 14 de agosto de 2012

Gostei tanto deste exemplo que quero melhorar com o HTML / CSS e a parte da batida

mude isso

//$("#heartbeat").show().fadeOut(1000); // just a little "red flash" in the corner :)

para dentro

beatHeart(2); // just a little "red flash" in the corner :)

e adicione

// beat the heart 
// 'times' (int): nr of times to beat
function beatHeart(times) {
    var interval = setInterval(function () {
        $(".heartbeat").fadeIn(500, function () {
            $(".heartbeat").fadeOut(500);
        });
    }, 1000); // beat every second

    // after n times, let's clear the interval (adding 100ms of safe gap)
    setTimeout(function () { clearInterval(interval); }, (1000 * times) + 100);
}

HTML e CSS

<div class="heartbeat">&hearts;</div>

/* HEARBEAT */
.heartbeat {
    position: absolute;
    display: none;
    margin: 5px;
    color: red;
    right: 0;
    top: 0;
}

aqui está um exemplo ao vivo apenas para a parte de espancamento: http://jsbin.com/ibagob/1/

Veggerby
fonte
@veggerby "para um manipulador HTTP fictício que não faz nada além de manter minha sessão viva". Você pode postar um código de amostra do HTTP Handler para manter a sessão ativa?
Gopinath
não, e sua única opção (eu acho) é aumentar o tempo limite da sessão, mas isso provavelmente é uma má ideia a longo prazo
veggerby
Estava fazendo investigações sobre o problema relacionado e encontrei essa solução. Coisa boa. Uma consulta, porém, se o usuário deixar o navegador aberto e disser que o pc não dorme por 10 horas, a sessão será mantida ativa por tanto tempo, certo? Isto está certo?
Julius A
2
Bom trabalho, mas falhou até que adicionei um estouro de cache à chamada. Sem este parâmetro de burst de cache, o controlador é chamado apenas pela primeira vez
jean
1
@stom deve ser um valor de sessão, não um tempo limite ou qualquer coisa. O motivo para usar DateTime.Nowera apenas para deixar claro quando a sessão foi atualizada pela última vez por meio da pulsação.
veggerby
69

Se você estiver usando ASP.NET MVC - você não precisa de um manipulador HTTP adicional e algumas modificações do arquivo web.config. Tudo que você precisa - apenas para adicionar alguma ação simples em um controlador Home / Comum:

[HttpPost]
public JsonResult KeepSessionAlive() {
    return new JsonResult {Data = "Success"};
}

, escreva um código JavaScript como este (coloquei em um dos arquivos JavaScript do site):

var keepSessionAlive = false;
var keepSessionAliveUrl = null;

function SetupSessionUpdater(actionUrl) {
    keepSessionAliveUrl = actionUrl;
    var container = $("#body");
    container.mousemove(function () { keepSessionAlive = true; });
    container.keydown(function () { keepSessionAlive = true; });
    CheckToKeepSessionAlive();
}

function CheckToKeepSessionAlive() {
    setTimeout("KeepSessionAlive()", 5*60*1000);
}

function KeepSessionAlive() {
    if (keepSessionAlive && keepSessionAliveUrl != null) {
        $.ajax({
            type: "POST",
            url: keepSessionAliveUrl,
            success: function () { keepSessionAlive = false; }
        });
    }
    CheckToKeepSessionAlive();
}

e inicialize essa funcionalidade chamando uma função JavaScript:

SetupSessionUpdater('/Home/KeepSessionAlive');

Observe! Implementei essa funcionalidade apenas para usuários autorizados (não há razão para manter o estado da sessão para convidados na maioria dos casos) e a decisão de manter o estado da sessão ativo não se baseia apenas em - o navegador está aberto ou não, mas o usuário autorizado deve realizar alguma atividade no site (mova o mouse ou digite alguma tecla).

Maryan
fonte
3
Para MVC, acho que esta é a melhor resposta. Não há necessidade de usar o arquivo .ashx, por que você faria isso?
arame3333
Com HTTP Handler no asp mvc verifique isso , espero que ajude alguém.
shaijut de
3
Maryan, você também pode querer usar @Url.Action("KeepSessionAlive","Home")na função de inicializador para não ter que codificar a URL e também lançar o primeiro bloco dentro de um IIFE e apenas exportar, uma SetupSessionUpdatervez que é a única coisa que precisa ser chamada externamente - algo como este: SessionUpdater.js
KyleMit
Existe uma razão pela qual setInterval não é usado? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier
8

Sempre que você faz uma solicitação ao servidor, o tempo limite da sessão é redefinido. Portanto, você pode apenas fazer uma chamada ajax para um manipulador HTTP vazio no servidor, mas certifique-se de que o cache do manipulador esteja desabilitado, caso contrário, o navegador armazenará seu manipulador em cache e não fará uma nova solicitação.

KeepSessionAlive.ashx.cs

public class KeepSessionAlive : IHttpHandler, IRequiresSessionState
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
            context.Response.Cache.SetNoStore();
            context.Response.Cache.SetNoServerCaching();
        }
    }

.JS:

window.onload = function () {
        setInterval("KeepSessionAlive()", 60000)
}

 function KeepSessionAlive() {
 url = "/KeepSessionAlive.ashx?";
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", url, true);
        xmlHttp.send();
        }

@veggerby - Não há necessidade de sobrecarga de armazenamento de variáveis ​​na sessão. Basta fazer uma solicitação ao servidor.

BornToCode
fonte
2

Você realmente precisa manter a sessão (você tem dados nela?) Ou é suficiente fingir isso reinstanciando a sessão quando uma solicitação chega? Se for o primeiro, use o método acima. Se for o segundo, tente algo como usar o manipulador de eventos Session_End.

Se você tiver autenticação de formulários, obterá algo em Global.asax.cs como

FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(formsCookie.Value);
if (ticket.Expired)
{
    Request.Cookies.Remove(FormsAuthentication.FormsCookieName);
    FormsAuthentication.SignOut();
    ...             
     }
else
{   ...
    // renew ticket if old
    ticket = FormsAuthentication.RenewTicketIfOld(ticket);
    ...
     }

E você define o tempo de vida do tíquete muito mais do que o da sessão. Se você não estiver autenticando ou usando um método de autenticação diferente, existem truques semelhantes. A interface da web do Microsoft TFS e o SharePoint parecem usar isso - a verdade é que, se você clicar em um link em uma página desatualizada, receberá prompts de autenticação na janela pop-up, mas se você apenas usar um comando, funciona.

Henry Troup
fonte
2

você pode simplesmente escrever este código em seu arquivo de script java, é isso.

$(document).ready(function () {
        var delay = (20-1)*60*1000;
        window.setInterval(function () {
            var url = 'put the url of some Dummy page';
            $.get(url);                
        }, delay);
});

Este (20-1)*60*1000é o tempo de atualização, ele irá atualizar o tempo limite da sessão. O tempo limite de atualização é calculado como tempo limite padrão de iis = 20 minutos, significa 20 × 60.000 = 1.200.000 milissegundos - 60.000 milissegundos (um minuto antes de a sessão expirar) é 1140000.

ANKIT KISHOR
fonte
0

Aqui está uma solução alternativa que deve sobreviver se o PC cliente entrar no modo de hibernação.

Se você tiver uma grande quantidade de usuários conectados, use isso com cautela, pois isso pode consumir muita memória do servidor.

Depois de fazer o login (eu faço isso no evento LoggedIn do controle de login)

Dim loggedOutAfterInactivity As Integer = 999 'Minutes

'Keep the session alive as long as the authentication cookie.
Session.Timeout = loggedOutAfterInactivity

'Get the authenticationTicket, decrypt and change timeout and create a new one.
Dim formsAuthenticationTicketCookie As HttpCookie = _
        Response.Cookies(FormsAuthentication.FormsCookieName)

Dim ticket As FormsAuthenticationTicket = _
        FormsAuthentication.Decrypt(formsAuthenticationTicketCookie.Value)
Dim newTicket As New FormsAuthenticationTicket(
        ticket.Version, ticket.Name, ticket.IssueDate, 
        ticket.IssueDate.AddMinutes(loggedOutAfterInactivity), 
        ticket.IsPersistent, ticket.UserData)
formsAuthenticationTicketCookie.Value = FormsAuthentication.Encrypt(newTicket)
Peter
fonte
Por que isso foi rejeitado? há algum tipo de problema que eu não mencionei? se for esse o caso, por favor, compartilhe para que os futuros leitores estejam cientes disso!
Peter de
0

Passei alguns dias tentando descobrir como prolongar a sessão de um usuário em WebForms por meio de uma caixa de diálogo pop-up, dando ao usuário a opção de renovar a sessão ou permitir que ela expire. A primeira coisa que você precisa saber é que não precisa de nenhuma dessas coisas sofisticadas de 'HttpContext' acontecendo em algumas das outras respostas. Tudo que você precisa é $ .post () do jQuery; método. Por exemplo, durante a depuração, usei:

$.post("http://localhost:5562/Members/Location/Default.aspx");

e em seu site ativo você usaria algo como:

$.post("http://mysite/Members/Location/Default.aspx");

É tão fácil quanto isso. Além disso, se desejar solicitar ao usuário a opção de renovar a sessão, faça algo semelhante ao seguinte:

    <script type="text/javascript">
    $(function () { 
        var t = 9;
        var prolongBool = false;
        var originURL = document.location.origin;
        var expireTime = <%= FormsAuthentication.Timeout.TotalMinutes %>;

        // Dialog Counter
        var dialogCounter = function() {
            setTimeout( function() {
                $('#tickVar').text(t);
                    t--;
                    if(t <= 0 && prolongBool == false) {
                        var originURL = document.location.origin;
                        window.location.replace(originURL + "/timeout.aspx");
                        return;
                    }
                    else if(t <= 0) {
                        return;
                    }
                    dialogCounter();
            }, 1000);
        }

        var refreshDialogTimer = function() {
            setTimeout(function() { 
                $('#timeoutDialog').dialog('open');
            }, (expireTime * 1000 * 60 - (10 * 1000)) );
        };

        refreshDialogTimer();

        $('#timeoutDialog').dialog({
            title: "Session Expiring!",
            autoOpen: false,
            height: 170,
            width: 350,
            modal: true,
            buttons: {
                'Yes': function () {
                    prolongBool = true;
                    $.post("http://localhost:5562/Members/Location/Default.aspx"); 
                    refreshDialogTimer();
                    $(this).dialog("close");
                },
                Cancel: function () {
                    var originURL = document.location.origin;
                    window.location.replace(originURL + "/timeout.aspx");
                }
            },
            open: function() {
                prolongBool = false;
                $('#tickVar').text(10);
                t = 9;
                dialogCounter();
            }
        }); // end timeoutDialog
    }); //End page load
</script>

Não se esqueça de adicionar a caixa de diálogo ao seu html:

        <div id="timeoutDialog" class='modal'>
            <form>
                <fieldset>
                    <label for="timeoutDialog">Your session will expire in</label>
                    <label for="timeoutDialog" id="tickVar">10</label>
                    <label for="timeoutDialog">seconds, would you like to renew your session?</label>
                </fieldset>
            </form>
        </div>
Colin
fonte
0

Em relação à solução veggerby, se você está tentando implementá-la em um aplicativo VB, tome cuidado ao tentar executar o código fornecido por meio de um tradutor. O seguinte funcionará:

Imports System.Web
Imports System.Web.Services
Imports System.Web.SessionState

Public Class SessionHeartbeatHttpHandler
    Implements IHttpHandler
    Implements IRequiresSessionState

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        context.Session("Heartbeat") = DateTime.Now
    End Sub
End Class

Além disso, em vez de chamar a função heartbeat () como:

 setTimeout("heartbeat()", 300000);

Em vez disso, chame-o assim:

 setInterval(function () { heartbeat(); }, 300000);

Número um, setTimeout dispara apenas uma vez, enquanto setInterval dispara repetidamente. Número dois, chamar heartbeat () como uma string não funcionou para mim, enquanto chamá-lo como uma função real funcionou.

E posso confirmar 100% que esta solução superará a decisão ridícula do GoDaddy de forçar uma sessão de apppool de 5 minutos no Plesk!

ShowJX1990
fonte
0

Aqui, a versão do plugin JQuery da solução Maryan com otimização de alças. Apenas com JQuery 1.7+!

(function ($) {
    $.fn.heartbeat = function (options) {
        var settings = $.extend({
            // These are the defaults.
            events: 'mousemove keydown'
            , url: '/Home/KeepSessionAlive'
            , every: 5*60*1000
        }, options);

        var keepSessionAlive = false
         , $container = $(this)
         , handler = function () {
             keepSessionAlive = true;
             $container.off(settings.events, handler)
         }, reset = function () {
             keepSessionAlive = false;
             $container.on(settings.events, handler);
             setTimeout(sessionAlive, settings.every);
         }, sessionAlive = function () {
             keepSessionAlive && $.ajax({
                 type: "POST"
                 , url: settings.url
                 ,success: reset
                });
         };
        reset();

        return this;
    }
})(jQuery)

e como isso importa em seu arquivo * .cshtml

$('body').heartbeat(); // Simple
$('body').heartbeat({url:'@Url.Action("Home", "heartbeat")'}); // different url
$('body').heartbeat({every:6*60*1000}); // different timeout
AlexPalla
fonte
0

[Atrasado para a festa ...]

Outra maneira de fazer isso sem a sobrecarga de uma chamada Ajax ou manipulador WebService é carregar uma página ASPX especial após um determinado período de tempo (ou seja, antes do tempo limite do estado da sessão, que normalmente é de 20 minutos):

// Client-side JavaScript
function pingServer() {
    // Force the loading of a keep-alive ASPX page
    var img = new Image(1, 1);
    img.src = '/KeepAlive.aspx';
}

A KeepAlive.aspxpágina é simplesmente uma página vazia que não faz nada além de tocar / atualizar o Sessionestado:

// KeepAlive.aspx.cs
public partial class KeepSessionAlive: System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Refresh the current user session
        Session["refreshTime"] = DateTime.UtcNow;
    }
}

Isso funciona criando um imgelemento (imagem) e forçando o navegador a carregar seu conteúdo da KeepAlive.aspxpágina. Carregar essa página faz com que o servidor toque (atualize) o Sessionobjeto, estendendo a janela de tempo de expiração da sessão (normalmente por mais 20 minutos). O conteúdo real da página da web é descartado pelo navegador.

Uma forma alternativa, e talvez mais limpa, de fazer isso é criar um novo iframeelemento e carregar a KeepAlive.aspxpágina nele. O iframeelemento é oculto, por exemplo, tornando-o um elemento filho de um divelemento oculto em algum lugar da página.

A atividade na própria página pode ser detectada interceptando ações do mouse e do teclado para todo o corpo da página:

// Called when activity is detected
function activityDetected(evt) {
    ...
}

// Watch for mouse or keyboard activity
function watchForActivity() {
    var opts = { passive: true };
    document.body.addEventListener('mousemove', activityDetected, opts);
    document.body.addEventListener('keydown', activityDetected, opts);
}

Não posso levar o crédito por essa ideia; consulte: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .

David R Tribble
fonte