2009-09-16 7 views
97

Quelle est la manière la plus simple et la plus discrète de maintenir une session ASP.NET active tant que la fenêtre du navigateur de l'utilisateur est ouverte? Est-ce que les appels AJAX sont chronométrés? Je veux éviter ce qui suit: parfois, les utilisateurs gardent leur fenêtre ouverte pendant un long moment, puis saisissent des choses, et rien ne fonctionne plus parce que la session côté serveur a expiré. Je ne veux pas augmenter la valeur du délai d'attente de plus de 10 minutes sur le serveur car je souhaite que les sessions fermées (en fermant la fenêtre du navigateur) expirent rapidement.Conserver la session ASP.NET ouverte/en vie

Suggestions, échantillons de code?

+0

Vous pouvez vérifier ce lien pour obtenir une réponse aussi bien http://www.dotnetcurry.com/ShowArticle.aspx?ID=453 –

Répondre

156

J'utilise JQuery pour effectuer un simple appel AJAX à un gestionnaire HTTP factice qui ne fait que garder ma session en vie:

function setHeartbeat() { 
    setTimeout("heartbeat()", 300000); // 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" 
    ); 
} 

gestionnaire de session peut être aussi simple que:

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

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

Le La clé est d'ajouter IRequiresSessionState, sinon Session ne sera pas disponible (= null). Le gestionnaire peut bien sûr également renvoyer un objet sérialisé JSON si certaines données doivent être renvoyées au JavaScript appelant.

mis à disposition par web.config:

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

ajouté de balexandre le 14 Août, 2012

J'aimais tant de cet exemple, que je veux améliorer la HTML/CSS et la partie de battement

modifiez cette

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

dans

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

et ajouter

// 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 et CSS

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

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

ici est un exemple en direct pour seulement la partie battant: http://jsbin.com/ibagob/1/

+21

+1 pour le nom cool – Martin

+2

+1 et + acc pour une bonne solution avec le code. – Alex

+0

@veggerby "à un gestionnaire HTTP factice qui ne fait rien mais maintient ma session en vie". Pouvez-vous s'il vous plaît envoyer un exemple de code de HTTP Handler pour maintenir la session en vie? – Gopinath

2

Avez-vous vraiment besoin de conserver la session (avez-vous des données dedans?) Ou est-ce suffisant pour simuler cela en réinstanciant la session quand une requête arrive? Si le premier, utilisez la méthode ci-dessus. Si c'est le cas, essayez quelque chose comme utiliser le gestionnaire d'événements Session_End.

Si vous avez des formulaires d'authentification, vous obtenez quelque chose dans le Global.asax.cs comme

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

et que vous définissez la durée de vie de billets beaucoup plus longue que la durée de vie de la session. Si vous ne vous authentifiez pas ou si vous utilisez une méthode d'authentification différente, il existe des astuces similaires. L'interface Web de Microsoft TFS et SharePoint semblent les utiliser - le fait est que si vous cliquez sur un lien sur une page périmée, vous obtenez des invites d'authentification dans la fenêtre contextuelle, mais si vous utilisez simplement une commande, cela fonctionne.

50

Si vous utilisez ASP.NET MVC - vous n'avez pas besoin d'un gestionnaire HTTP supplémentaire et quelques modifications du fichier web.config. Tout ce que vous avez besoin - juste pour ajouter une action simple dans une maison/contrôleur commun:

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

, écrire un morceau de code JavaScript comme celui-ci (je l'ai mis dans l'un fichier JavaScript du 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()", 300000); 
} 

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

et initialiser cette fonctionnalité en appelant une fonction JavaScript:

SetupSessionUpdater('/Home/KeepSessionAlive'); 

S'il vous plaît noter! J'ai implémenté cette fonctionnalité uniquement pour les utilisateurs autorisés (il n'y a aucune raison de conserver l'état de session pour les invités) et la décision de garder l'état de session active n'est pas seulement basée sur - le navigateur est ouvert ou non. activité sur le site (déplacez une souris ou tapez une touche).

+1

Pour MVC, je pense que c'est la meilleure réponse. Pas besoin d'utiliser le fichier .ashx, pourquoi le feriez-vous? – arame3333

+0

Avec ** gestionnaire HTTP dans asp mvc ** vérifier [this] (http://www.dotnetcurry.com/ShowArticle.aspx?ID=453), l'espoir aide quelqu'un. – stom

+1

Maryan, vous pouvez également utiliser @ Url.Action ("KeepSessionAlive", "Home") 'dans la fonction d'initialisation afin de ne pas avoir à coder en dur l'URL et aussi jeter le premier bloc à l'intérieur d'un IIFE et juste exporter le 'SetupSessionUpdater' puisque c'est la seule chose qui doit être invoquée en externe - quelque chose comme ceci: [SessionUpdater.js] (https://gist.github.com/KyleMit/aa4f576fa32bf36fbedab5540c18211d#file-sessionupdater-js) – KyleMit

6

Chaque fois que vous effectuez une requête sur le serveur, le délai d'expiration de la session est réinitialisé. Vous pouvez donc faire un appel ajax à un gestionnaire HTTP vide sur le serveur, mais assurez-vous que le cache du gestionnaire est désactivé, sinon le navigateur mettra en cache votre gestionnaire et ne fera pas de nouvelle demande.

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 - Il n'y a pas besoin de la surcharge de stockage des variables dans la session. Il suffit de préformer une requête sur le serveur.

0

Voici une solution alternative qui devrait survivre si le PC client passe en mode veille.

Si vous avez un grand nombre d'utilisateurs connectés, utilisez-le avec précaution, car cela pourrait consommer beaucoup de mémoire.

Une fois connecté (je le fais en cas LoggedIn du contrôle de connexion)

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) 
+0

Pourquoi ce bas a voté? Y at-il une sorte de problème avec ce que je n'ai pas mentionné? Si c'est le cas s'il vous plaît partager afin que les futurs lecteurs en soient conscients! – Peter

1

vous pouvez simplement écrire ce code dans votre fichier java script thats it. Le 1140000 est l'heure d'actualisation, il actualisera le délai d'expiration de la session. Le délai d'actualisation est calculé par défaut sur iis = 20 minutes, signifie 20 × 60000 = 1200000 millisecondes - 60000 millisecondes (une minute avant l'expiration de la session) est 1140000.

0

J'ai passé quelques jours à essayer de trouver comment prolonger une session utilisateurs dans WebForms via une boîte de dialogue contextuelle permettant à l'utilisateur de renouveler la session ou de l'autoriser à expirer. La chose n ° 1 que vous devez savoir est que vous n'avez pas besoin de ce truc «HttpContext» sophistiqué qui se passe dans certaines des autres réponses. Tout ce dont vous avez besoin est $ .post() de jQuery; méthode. Par exemple, pendant le débogage je:

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

et sur votre site en vous utiliseriez quelque chose comme:

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

Il est aussi simple que cela.De plus, si vous souhaitez inviter l'utilisateur avec la possibilité de renouveler leur session de faire quelque chose de semblable à ce qui suit:

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

Ne pas oublier d'ajouter la boîte de dialogue à votre 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

Voici la version du plugin JQuery de la solution Maryan avec optimisation de la poignée. Seulement avec JQuery 1.7+!

(function ($) { 
    $.fn.heartbeat = function (options) { 
     var settings = $.extend({ 
      // These are the defaults. 
      events: 'mousemove keydown' 
      , url: '/Home/KeepSessionAlive' 
      , every: 300000 
     }, 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) 

et comment il fait importer dans votre * .cshtml

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