保持ASP.NET会话开放/有效


115

只要用户打开了浏览器窗口,哪种方法最容易且最不引起干扰的方式就可以使ASP.NET会话保持活动状态?是定时的AJAX通话吗?我要防止出现以下情况:有时用户长时间保持窗口打开状态,然后输入内容,并且由于服务器端会话已过期,因此提交后再无任何作用。我不想在服务器上将超时值增加超过10分钟,因为我希望关闭的会话(通过关闭浏览器窗口)来快速超时。

建议,代码示例?


您也可以检查此链接以获取答案dotnetcurry.com/ShowArticle.aspx?ID=453
开发人员

Answers:


170

我使用JQuery对虚拟HTTP处理程序执行简单的AJAX调用,该处理程序除了使我的Session保持活动之外什么都不做:

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

会话处理程序可以很简单:

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

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

关键是添加IRequiresSessionState,否则Session将不可用(= null)。如果某些数据应返回到调用JavaScript,则处理程序当然也可以返回JSON序列化对象。

通过web.config提供:

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

添加balexandre于2012年8月14日

我非常喜欢这个示例,因此我想通过HTML / CSS和Beat部分进行改进

改变这个

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

进入

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

并添加

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

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

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

这是仅一个跳动部分的实时示例http : //jsbin.com/ibagob/1/


@veggerby“到一个不执行任何操作但使我的会话保持活动状态的虚拟HTTP处理程序”。您可以张贴HTTP处理程序的示例代码来保持会话存活吗?
Gopinath

不,您唯一的选择(我想)是增加会话超时,但是从长远来看,这可能不是一个好主意
veggerby 2011年

正在对相关问题进行调查,并发现了该解决方案。好东西。但是,有一个查询,如果用户保持浏览器打开,并且说PC不能在10个小时内进入睡眠状态,那么会话将保持如此长的时间,对吗?这是正确的吗?
朱利叶斯

2
干得好,但是直到我在呼叫中添加了缓存突发之后,它还是失败了。如果没有此缓存突发参数,则控制器将仅被首次调用
吉恩

1
@stom它意味着是一个会话值,而不是一个超时或任何东西。使用该文件的原因DateTime.Now只是为了使该会话最近一次通过心跳更新的时间很明显。
veggerby

69

如果您使用的是ASP.NET MVC,则不需要其他HTTP处理程序和对web.config文件的某些修改。您所需要的–仅在Home / Common控制器中添加一些简单的操作即可:

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

,编写一段像这样的JavaScript代码(我将其放在站点的JavaScript文件之一中):

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

,并通过调用JavaScript函数来初始化此功能:

SetupSessionUpdater('/Home/KeepSessionAlive');

请注意!我仅对授权用户实施了此功能(在大多数情况下,没有理由为访客保持会话状态),并且决定保持会话状态为活动状态不仅取决于–是否打开了浏览器,而且授权用户必须执行某些操作在网站上(移动鼠标或键入一些键)。


3
对于MVC,我认为这是更好的答案。无需使用.ashx文件,为什么呢?
arame3333 '16

随着HTTP处理程序在ASP MVC检查,希望可以帮助别人。
shaijut

3
Maryan,您可能还想@Url.Action("KeepSessionAlive","Home")在初始化函数中使用,这样就不必对URL进行硬编码,也不必将第一个块扔到IIFE内并只需导出,SetupSessionUpdater因为这是唯一需要在外部调用的东西-例如这:SessionUpdater.js
KyleMit '16

有没有理由不使用setInterval的原因? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier

8

每当您向服务器发出请求时,会话超时都会重置。因此,您可以仅对服务器上的空HTTP处理程序进行ajax调用,但请确保已禁用该处理程序的缓存,否则浏览器将缓存您的处理程序,并且不会发出新请求。

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-无需在会话中存储变量的开销。仅向服务器执行请求就足够了。


2

您是否真的需要保留会话(其中是否有数据?)还是通过在请求进入时重新实例化会话来伪造它?如果是第一种,请使用上面的方法。如果是第二种,请尝试使用Session_End事件处理程序。

如果您具有“表单身份验证”,那么您会在Global.asax.cs中得到类似

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

并且您将票证生命周期设置为比会话生命周期更长。如果您不进行身份验证,或使用其他身份验证方法,则有类似的技巧。Microsoft TFS Web界面和SharePoint似乎使用了这些-所带来的好处是,如果您单击陈旧页面上的链接,则会在弹出窗口中看到身份验证提示,但是如果您仅使用命令,它就会起作用。


2

您可以只在您的Java脚本文件中编写此代码。

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

(20-1)*60*1000是刷新时间,这将刷新会话超时。刷新超时的计算公式为:iis = 20分钟,即20×60000 = 1200000毫秒-60000毫秒(会话到期前一分钟)的默认超时时间是1140000。


0

如果客户端PC进入睡眠模式,则这是一种可以保留的替代解决方案。

如果您有大量的登录用户,请谨慎使用,因为这可能会占用大量服务器内存。

登录后(我在登录控件的LoggedIn事件中执行此操作)

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

我花了几天时间试图弄清楚如何通过弹出对话框延长WebForms中的用户会话,该对话框为用户提供了续订会话或使其过期的选项。您需要知道的第一件事是,您不需要在其他一些答案中使用任何花哨的“ HttpContext”东西。您只需要jQuery的$ .post();。方法。例如,在调试时,我使用了:

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

在您的实时网站上,您将使用类似以下内容的代码:

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

就这么简单。此外,如果您想提示用户选择续订会话,请执行以下操作:

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

不要忘记将对话框添加到您的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

关于veggerby的解决方案,如果您尝试在VB应用程序上实现它,请小心尝试通过翻译器运行提供的代码。以下将起作用:

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

另外,与其像heartbeat()那样调用,不如:

 setTimeout("heartbeat()", 300000);

而是这样称呼它:

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

第一,setTimeout仅触发一次,而setInterval将重复触发。第二,像字符串一样调用heartbeat()对我不起作用,而像实际函数那样调用它。

我绝对可以100%确认此解决方案将克服GoDaddy的荒谬决定,即在Plesk中强制执行5分钟的应用程序会话!


0

这里是具有句柄优化的Maryan解决方案的JQuery插件版本。仅适用于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)

以及它如何导入* .cshtml

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

0

[晚会上...]

另一种没有Ajax调用或WebService处理程序开销的方法是在给定的时间后(即,在会话状态超时之前,通常是20分钟)加载特殊的ASPX页面:

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

KeepAlive.aspx页面只是一个空页面,除了触摸/刷新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;
    }
}

这是通过创建img(图像)元素并强制浏览器从KeepAlive.aspx页面加载其内容来实现的。加载该页面会导致服务器触摸(更新)Session对象,从而延长会话的过期滑动时间窗口(通常再延长20分钟)。实际的网页内容被浏览器丢弃。

一种替代的方法,也许是一种更清洁的方法,是创建一个新iframe元素并将KeepAlive.aspx页面加载到其中。该iframe元素是隐藏的,比如通过使一个隐藏的子元素div在页面上元素的地方。

通过拦截整个页面正文的鼠标和键盘操作,可以检测页面本身上的活动:

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

我不能相信这个主意。请参阅:https : //www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.