Mantenere la sessione ASP.NET aperta / attiva


116

Qual è il modo più semplice e discreto per mantenere viva una sessione ASP.NET finché l'utente ha la finestra del browser aperta? Sono chiamate AJAX a tempo? Voglio evitare quanto segue: a volte gli utenti tengono la loro finestra aperta per molto tempo, quindi inseriscono le cose e su submit non funziona più nulla perché la sessione lato server è scaduta. Non voglio aumentare il valore di timeout per più di 10 minuti sul server perché desidero che le sessioni chiuse (chiudendo la finestra del browser) scadano rapidamente.

Suggerimenti, esempi di codice?


Puoi controllare questo link per ottenere anche la risposta dotnetcurry.com/ShowArticle.aspx?ID=453
Sviluppatore

Risposte:


171

Uso JQuery per eseguire una semplice chiamata AJAX a un gestore HTTP fittizio che non fa altro che mantenere viva la mia sessione:

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"
    );
}

Il gestore della sessione può essere semplice come:

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

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

La chiave è aggiungere IRequiresSessionState, altrimenti Session non sarà disponibile (= null). Il gestore può ovviamente restituire anche un oggetto serializzato JSON se alcuni dati devono essere restituiti al JavaScript chiamante.

Reso disponibile tramite web.config:

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

aggiunto da balexandre il 14 agosto 2012

Mi è piaciuto così tanto questo esempio, che voglio migliorare con l'HTML / CSS e la parte beat

cambia questo

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

in

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

e aggiungi

// 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;
}

ecco un esempio dal vivo solo per la parte battente: http://jsbin.com/ibagob/1/


@veggerby "a un gestore HTTP fittizio che non fa altro che mantenere viva la mia sessione". Puoi inserire un codice di esempio del gestore HTTP per mantenere viva la sessione?
Gopinath

no, e la tua unica opzione (immagino) è aumentare il timeout della sessione, ma probabilmente è una cattiva idea a lungo termine
veggerby

Stava facendo indagini su problemi correlati e si è imbattuto in questa soluzione. Roba buona. Una domanda, però, se l'utente lascia il browser aperto e dice che il PC non va a dormire per 10 ore, la sessione sarà mantenuta in vita per così tanto tempo, giusto? È giusto?
Julius A

2
Bel lavoro, ma non è riuscito fino a quando non ho aggiunto un burst di cache alla chiamata. Senza questo parametro cache burst, il controller viene chiamato solo la prima volta
Jean

1
@stom è pensato per essere un valore di sessione, non un timeout o altro. Il motivo dell'utilizzo DateTime.Nowera solo per rendere evidente quando la sessione è stata aggiornata l'ultima volta tramite il battito cardiaco.
veggerby

69

Se si utilizza ASP.NET MVC, non è necessario un gestore HTTP aggiuntivo e alcune modifiche al file web.config. Tutto ciò di cui hai bisogno - solo per aggiungere qualche semplice azione in un controller Home / Comune:

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

, scrivi un pezzo di codice JavaScript come questo (l'ho inserito in uno dei file JavaScript del sito):

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 inizializza questa funzionalità chiamando una funzione JavaScript:

SetupSessionUpdater('/Home/KeepSessionAlive');

Notare che! Ho implementato questa funzionalità solo per gli utenti autorizzati (nella maggior parte dei casi non c'è motivo di mantenere lo stato della sessione per gli ospiti) e la decisione di mantenere attivo lo stato della sessione non si basa solo sul browser aperto o meno, ma l'utente autorizzato deve svolgere alcune attività sul sito (muovi il mouse o digita un tasto).


3
Per MVC penso che questa sia la risposta migliore. Non è necessario utilizzare il file .ashx, perché dovresti?
arame3333

Con il gestore HTTP in asp mvc controlla questo , la speranza aiuta qualcuno.
shaijut

3
Maryan, potresti anche volerlo utilizzare @Url.Action("KeepSessionAlive","Home")nella funzione di inizializzazione in modo da non dover codificare l'URL e anche lanciare il primo blocco all'interno di un IIFE ed esportare semplicemente il SetupSessionUpdaterpoiché è l'unica cosa che deve essere invocata esternamente - qualcosa di simile questo: SessionUpdater.js
KyleMit

C'è un motivo per cui setInterval non viene utilizzato? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier

8

Ogni volta che si effettua una richiesta al server, il timeout della sessione si azzera. Quindi puoi semplicemente fare una chiamata ajax a un gestore HTTP vuoto sul server, ma assicurati che la cache del gestore sia disabilitata, altrimenti il ​​browser memorizzerà nella cache il tuo gestore e non farà una nuova richiesta.

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 - Non è necessario l'overhead di memorizzare le variabili nella sessione. È sufficiente preformare una richiesta al server.


2

Hai davvero bisogno di mantenere la sessione (ci sono dati al suo interno?) O è sufficiente falsificarlo reinstanziando la sessione quando arriva una richiesta? Se il primo, usa il metodo sopra. Se il secondo, prova qualcosa come usare il gestore di eventi Session_End.

Se disponi dell'autenticazione basata su moduli, ottieni qualcosa in Global.asax.cs come

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 imposti la durata del ticket molto più lunga della durata della sessione. Se non stai effettuando l'autenticazione o stai utilizzando un metodo di autenticazione diverso, esistono trucchi simili. L'interfaccia Web di Microsoft TFS e SharePoint sembrano utilizzarli: il fatto è che se fai clic su un collegamento in una pagina obsoleta, ricevi richieste di autenticazione nella finestra popup, ma se usi solo un comando, funziona.


2

puoi semplicemente scrivere questo codice nel tuo file di script java questo è tutto.

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

È il (20-1)*60*1000tempo di aggiornamento, aggiornerà il timeout della sessione. Il timeout di aggiornamento viene calcolato come un timeout predefinito di iis = 20 minuti, significa che 20 × 60000 = 1200000 millisecondi - 60000 millisecondi (un minuto prima della scadenza della sessione) è 1140000.


0

Ecco una soluzione alternativa che dovrebbe sopravvivere se il PC client entra in modalità di sospensione.

Se hai una quantità enorme di utenti registrati, usalo con cautela poiché potrebbe consumare molta memoria del server.

Dopo aver effettuato l'accesso (lo faccio nell'evento LoggedIn del controllo di accesso)

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)

Perché è stato votato verso il basso? c'è qualche tipo di problema con esso che non ho menzionato? se è così per favore condividi in modo che i lettori futuri ne siano consapevoli!
Peter,

0

Ho trascorso alcuni giorni cercando di capire come prolungare una sessione utente in WebForms tramite una finestra di dialogo popup che offre all'utente la possibilità di rinnovare la sessione o di lasciarla scadere. La prima cosa che devi sapere è che non hai bisogno di nessuna di queste fantasiose cose 'HttpContext' in alcune delle altre risposte. Tutto ciò di cui hai bisogno è $ .post () di jQuery; metodo. Ad esempio, durante il debug ho usato:

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

e sul tuo sito live useresti qualcosa come:

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

È così facile. Inoltre, se desideri richiedere all'utente la possibilità di rinnovare la sessione, fai qualcosa di simile a quanto segue:

    <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>

Non dimenticare di aggiungere la finestra di dialogo al tuo 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>

0

Per quanto riguarda la soluzione di veggerby, se stai cercando di implementarla su un'app VB, fai attenzione a provare a eseguire il codice fornito tramite un traduttore. Funzionerà quanto segue:

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

Inoltre, invece di chiamare la funzione come heartbeat () come:

 setTimeout("heartbeat()", 300000);

Invece, chiamalo come:

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

Numero uno, setTimeout si attiva solo una volta mentre setInterval si attiva ripetutamente. Numero due, chiamare heartbeat () come una stringa non ha funzionato per me, mentre chiamarlo come una funzione effettiva.

E posso confermare al 100% che questa soluzione supererà la ridicola decisione di GoDaddy di forzare una sessione di apppool di 5 minuti in Plesk!


0

Qui la versione plug-in JQuery della soluzione Maryan con ottimizzazione dell'handle. Solo con 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 come importa nel tuo * .cshtml

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

0

[In ritardo alla festa ...]

Un altro modo per farlo senza il sovraccarico di una chiamata Ajax o di un gestore WebService è caricare una pagina ASPX speciale dopo un determinato periodo di tempo (ovvero prima del timeout dello stato della sessione, che in genere è di 20 minuti):

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

La KeepAlive.aspxpagina è semplicemente una pagina vuota che non fa altro che toccare / aggiornare lo Sessionstato:

// 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;
    }
}

Funziona creando un imgelemento (immagine) e costringendo il browser a caricare i suoi contenuti dalla KeepAlive.aspxpagina. Il caricamento di quella pagina fa sì che il server tocchi (aggiorni) l' Sessionoggetto, estendendo l'intervallo di tempo scorrevole di scadenza della sessione (in genere di altri 20 minuti). I contenuti effettivi della pagina web vengono eliminati dal browser.

Un modo alternativo, e forse più pulito, per farlo è creare un nuovo iframeelemento e caricarvi la KeepAlive.aspxpagina. L' iframeelemento è nascosto, ad esempio rendendolo un elemento figlio di un divelemento nascosto da qualche parte nella pagina.

L'attività sulla pagina stessa può essere rilevata intercettando le azioni del mouse e della tastiera per l'intero corpo della pagina:

// 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);
}

Non posso prendermi il merito di questa idea; vedere: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.