保持 ASP.NET 会话打开/活动

Posted

技术标签:

【中文标题】保持 ASP.NET 会话打开/活动【英文标题】:Keeping ASP.NET Session Open / Alive 【发布时间】:2010-11-28 17:44:06 【问题描述】:

只要用户打开浏览器窗口,哪一种方法是保持 ASP.NET 会话活动的最简单且最不显眼的方法?是定时 AJAX 调用吗?我想防止以下情况:有时用户将窗口打开很长时间,然后输入内容,然后提交时不再起作用,因为服务器端会话已过期。我不想在服务器上将超时值增加超过 10 分钟,因为我希望关闭的会话(通过关闭浏览器窗口)快速超时。

建议、代码示例?

【问题讨论】:

您也可以查看此链接以获取答案dotnetcurry.com/ShowArticle.aspx?ID=453 【参考方案1】:

我使用 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>

于 2012 年 8 月 14 日从balexandre 添加

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

改一下

//$("#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 处理程序的示例代码以保持会话活动? 不,你唯一的选择(我猜)是增加会话超时,但从长远来看这可能是个坏主意 正在对相关问题进行调查并遇到此解决方案。好东西。但是有一个查询,如果用户让他们的浏览器保持打开状态,并说 pc 没有进入睡眠 10 小时,那么会话将保持活动这么长时间,对吗?对吗? 干得好,但在我向调用中添加缓存突发之前它失败了。如果没有这个缓存突发参数,控制器只会在第一次被调用 @stom 意味着是一个会话值,而不是超时或任何东西。使用DateTime.Now 的原因只是为了让会话上次通过心跳更新的时间变得明显。【参考方案2】:

如果您使用的是 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');

请注意!我仅为授权用户实现了此功能(在大多数情况下,没有理由为访客保持会话状态),保持会话状态处于活动状态的决定不仅基于 -浏览器是否打开,但授权用户必须在网站上进行一些活动(移动鼠标或键入一些键)。

【讨论】:

对于 MVC,我认为这是更好的答案。不需要使用 .ashx 文件,为什么? With HTTP Handler in asp mvc检查this,希望对某人有所帮助。 Maryan,您可能还想在初始化函数中使用@Url.Action("KeepSessionAlive","Home"),这样您就不必对 URL 进行硬编码,也不必将第一个块放入 IIFE 中,只需导出 SetupSessionUpdater因为这是唯一需要在外部调用的东西 - 像这样:SessionUpdater.js 不使用setInterval有什么原因吗? setInterval(KeepSessionAlive, 300000)【参考方案3】:

每当您向服务器发出请求时,会话超时都会重置。所以你可以在服务器上对一个空的 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 - 不需要在会话中存储变量的开销。只需向服务器发出请求就足够了。

【讨论】:

【参考方案4】:

您真的需要保留会话(其中是否有数据?)还是通过在请求进入时重新实例化会话来伪造这一点就足够了?如果是第一种,请使用上述方法。如果是第二个,请尝试使用 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 似乎使用这些 - 赠送的是,如果您单击过时页面上的链接,您会在弹出窗口中获得身份验证提示,但如果您只使用命令,它就可以工作。

【讨论】:

【参考方案5】:

你可以把这段代码写在你的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。

【讨论】:

【参考方案6】:

[聚会迟到了...]

另一种无需 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 元素的子元素。

可以通过拦截整个页面主体的鼠标和键盘动作来检测页面本身的Activity:

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

【讨论】:

【参考方案7】:

如果客户端电脑进入睡眠模式,这是一个替代解决方案。

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

登录后(我在登录控件的 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)

【讨论】:

为什么这被否决了?有什么我没有提到的问题吗?如果是这种情况,请分享,以便未来的读者知道!【参考方案8】:

我花了几天时间试图弄清楚如何通过弹出对话框延长 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>

【讨论】:

【参考方案9】:

关于 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 分钟应用程序池会话的荒谬决定!

【讨论】:

【参考方案10】:

这里是带有句柄优化的 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

【讨论】:

以上是关于保持 ASP.NET 会话打开/活动的主要内容,如果未能解决你的问题,请参考以下文章

ajax 请求是不是会导致更新 asp.net 会话?

如何在 ASP.NET Core 中保持对象活动?

重置会话超时而不在 ASP.Net 中进行回发

asp.net bootstrap 在回发事件后保持当前活动选项卡

Asp.net 会话越界/混淆

如何在回发 ASP.Net Core 后保持选项卡处于活动状态