Quel est le moyen le plus simple et le plus discret de conserver une session ASP.NET en vie tant que l'utilisateur a ouvert la fenêtre du navigateur? 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 éléments et, ensuite, rien ne fonctionne plus, car la session côté serveur a expiré. Je ne souhaite pas augmenter la valeur du délai d'attente pendant plus de 10 minutes sur le serveur, car je souhaite que les sessions fermées (en fermant la fenêtre du navigateur) expirent rapidement.
Suggestions, exemples de code?
J'utilise JQuery pour effectuer un simple appel AJAX à un gestionnaire HTTP factice qui ne fait que garder ma session active:
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"
);
}
Le 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;
}
}
La clé consiste à ajouter IRequiresSessionState, sinon la session ne sera pas disponible (= null). Bien entendu, le gestionnaire peut également renvoyer un objet sérialisé JSON si certaines données doivent être renvoyées au JavaScript appelant.
Disponible via web.config:
<httpHandlers>
<add verb="GET,HEAD" path="SessionHeartbeat.ashx" validate="false" type="SessionHeartbeatHttpHandler"/>
</httpHandlers>
ajouté de balexandre le 14 août 2012
J'ai tellement aimé cet exemple que je veux m'améliorer avec HTML/CSS et la partie beat
change ça
//$("#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">♥</div>
/* HEARBEAT */
.heartbeat {
position: absolute;
display: none;
margin: 5px;
color: red;
right: 0;
top: 0;
}
voici un exemple live pour la seule partie frappante: http://jsbin.com/ibagob/1/
Si vous utilisez ASP.NET MVC, vous n'avez pas besoin d'un gestionnaire HTTP supplémentaire ni de certaines modifications du fichier web.config. Tout ce dont vous avez besoin - juste pour ajouter une action simple dans un contrôleur Home/Common:
[HttpPost]
public JsonResult KeepSessionAlive() {
return new JsonResult {Data = "Success"};
}
, écrivez un morceau de code JavaScript comme celui-ci (je l’ai mis dans l’un des fichiers 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 initialisez cette fonctionnalité en appelant une fonction JavaScript:
SetupSessionUpdater('/Home/KeepSessionAlive');
Notez s'il vous plaît! 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 dans la plupart des cas) et la décision de maintenir l'état de session actif n'est pas uniquement basée sur - le navigateur est-il ouvert ou non, mais l'utilisateur autorisé doit le faire une activité sur le site (déplacez une souris ou tapez une clé).
Chaque fois que vous faites une demande au serveur, le délai d'expiration de la session est réinitialisé. Vous pouvez donc simplement appeler un ajax vers un gestionnaire HTTP vide sur le serveur, mais assurez-vous que le cache du gestionnaire est désactivé, sinon le navigateur mettra votre gestionnaire en cache 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'est pas nécessaire de surcharger le stockage des variables dans la session. Il suffit de préformer une requête auprès du serveur.
vous pouvez simplement écrire ce code dans votre fichier de script Java, c'est tout.
$(document).ready(function () {
window.setInterval(function () {
var url = 'put the url of some Dummy page';
$.get(url);
},1140000);
});
Le 1140000 est l'heure d'actualisation, il actualisera le délai d'attente de la session. Le délai d'actualisation est calculé comme suit: le délai d'expiration par défaut = 20 minutes, ce qui signifie 20 × 60000 = 1200000 millisecondes - 60000 millisecondes (une minute avant l'expiration de la session) est de 1140000.
Avez-vous vraiment besoin de conserver la session (y a-t-il des données?) Ou est-ce suffisant de simuler cela en le remettant à jour en cas de demande? Si le premier, utilisez la méthode ci-dessus. Dans le second cas, essayez d'utiliser le gestionnaire d'événements Session_End.
Si vous avez l'authentification par formulaire, alors vous obtenez quelque chose dans le fichier 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 vous définissez la durée de vie du ticket beaucoup plus longue que celle de la session. Si vous n'authentifiez pas ou n'utilisez pas une méthode d'authentification différente, des astuces similaires existent. L’interface Web Microsoft TFS et SharePoint semblent les utiliser. En d’autres termes, si vous cliquez sur un lien d’une page obsolète, des invites d’authentification apparaissent dans la fenêtre contextuelle, mais si vous utilisez simplement une commande, cela fonctionne.
Voici une solution alternative qui devrait survivre si le PC client passe en mode veille.
Si vous avez un nombre considérable d’utilisateurs connectés, utilisez-le avec prudence, car cela pourrait consommer beaucoup de mémoire serveur.
Après votre connexion (je le fais dans l'événement 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)
En ce qui concerne la solution de veggerby, si vous essayez de l'implémenter sur une application VB, faites attention à ne pas exécuter le code fourni via un traducteur. Ce qui suit fonctionnera:
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
Aussi, au lieu d’appeler comme heartbeat (), la fonction est la suivante:
setTimeout("heartbeat()", 300000);
Appelez-le plutôt comme suit:
setInterval(function () { heartbeat(); }, 300000);
Premièrement, setTimeout ne se déclenche qu'une seule fois, tandis que setInterval se déclenche à plusieurs reprises. Deuxièmement, appeler heartbeat () comme une chaîne ne fonctionnait pas pour moi, alors que l'appeler comme une fonction réelle fonctionnait.
Et je peux absolument confirmer à 100% que cette solution surmontera la décision ridicule de GoDaddy de forcer une session d'apppool de 5 minutes à Plesk!
J'ai passé quelques jours à essayer de comprendre comment prolonger une session d'utilisateurs dans WebForms via une boîte de dialogue contextuelle donnant à l'utilisateur la possibilité de renouveler la session ou de lui permettre de expirer. La première chose que vous devez savoir, c'est que vous n'avez besoin d'aucun de ces éléments fantaisistes 'HttpContext' dans d'autres réponses. Tout ce dont vous avez besoin, c'est $ .post () de jQuery; méthode. Par exemple, lors du débogage, j'ai utilisé:
$.post("http://localhost:5562/Members/Location/Default.aspx");
et sur votre site live, vous utiliseriez quelque chose comme:
$.post("http://mysite/Members/Location/Default.aspx");
C'est aussi simple que ça. De plus, si vous souhaitez inviter l'utilisateur à renouveler sa session, procédez comme 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>
N'oubliez pas d'ajouter le dialogue à votre code 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>
Voici la version du plugin JQuery de la solution Maryan avec optimisation des poignées. 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 cela importe dans votre * .cshtml
$('body').heartbeat(); // Simple
$('body').heartbeat({url:'@Url.Action("Home", "heartbeat")'}); // different url
$('body').heartbeat({every:400000}); // different timeout
[En retard à la fête ...]
Une autre façon de le faire sans la surcharge d'un appel Ajax ou d'un gestionnaire WebService consiste à charger une page ASPX spéciale après une durée donnée (c'est-à-dire avant l'expiration du délai d'attente de la session, qui est généralement de 20 minutes):
// 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 page KeepAlive.aspx
est simplement une page vide qui ne fait que toucher/actualiser l'état Session
:
// 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;
}
}
Cela fonctionne en créant un élément img
(image) et en forçant le navigateur à charger son contenu à partir de la page KeepAlive.aspx
. Le chargement de cette page force le serveur à toucher (mettre à jour) l'objet Session
, prolongeant ainsi la fenêtre de la période de temps d'expiration de la session (généralement de 20 minutes supplémentaires). Le contenu réel de la page Web est ignoré par le navigateur.
L'activité sur la page elle-même peut être détectée en interceptant les actions de la souris et du clavier pour le corps entier de la page:
// 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);
}
Je ne peux pas prendre le crédit pour cette idée; voir: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .