Сохранение сеанса ASP.NET Open/Alive

Какой самый простой и ненавязчивый способ сохранить сеанс ASP.NET до тех пор, пока у пользователя открыто окно браузера? Приурочены ли звонки AJAX? Я хочу предотвратить следующее: иногда пользователи оставляют свое окно открытым в течение длительного времени, а затем вводят материал, и после отправки ничего не работает, потому что сеанс на стороне сервера истек. Я не хочу увеличивать значение тайм-аута более 10 минут на сервере, так как я хочу, чтобы закрытые сеансы (закрывая окно браузера) быстро терялись.

Предложения, примеры кода?

Ответ 1

Я использую JQuery для выполнения простого AJAX-вызова фиктивного обработчика HTTP, который не выполняет ничего, кроме поддержания моей сессии:

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 будет недоступен (= ноль). Конечно, обработчик также может возвращать сериализованный объект JSON, если некоторые данные должны быть возвращены вызывающему JavaScript.

Сделано доступным через web.config:

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

добавлено из balexandre 14 августа 2012 г.

Мне очень понравился этот пример, и я хочу улучшить его с помощью 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 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/

Ответ 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');

Обратите внимание! Я реализовал эту функцию только для авторизованных пользователей (в большинстве случаев нет оснований сохранять состояние сеанса для гостей), и решение поддерживать состояние сеанса не только на основании - открыт ли браузер или нет, но авторизованный пользователь должен выполнять некоторые действия на сайте (перемещать мышь или вводить какую-либо клавишу).

Ответ 3

Всякий раз, когда вы делаете запрос на сервер, тайм-аут сеанса сбрасывается. Таким образом, вы можете просто сделать вызов ajax на пустой обработчик HTTP на сервере, но убедитесь, что кеш обработчика отключен, иначе браузер будет кэшировать ваш обработчик и не будет делать новый запрос.

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

Вот альтернативное решение, которое должно выжить, если клиентский компьютер переходит в спящий режим.

Если у вас есть огромное количество зарегистрированных пользователей, используйте это с осторожностью, так как это может съесть много памяти сервера.

После входа в систему (я делаю это в событии 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)

Ответ 7

Я потратил несколько дней, пытаясь выяснить, как продлить сеанс пользователей в WebForms через всплывающее диалоговое окно, дающее пользователю возможность возобновить сеанс или разрешить его истечение. # 1 вещь, которую вам нужно знать, это то, что вам не нужны какие-либо из этих причудливых материалов "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>

Ответ 8

Здесь JQuery плагин версия решения Maryan с ручкой оптимизации. Только с 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

Ответ 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 о принудительном 5-минутном сеансе apppool в Plesk!

Ответ 10

[Поздно на вечеринку...]

Другой способ сделать это без накладных расходов на вызов Ajax или обработчик WebService - это загрузить специальную страницу ASPX через заданный промежуток времени (то есть до истечения времени ожидания сеанса, которое обычно составляет 20 минут):

// 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 минут). Фактическое содержимое веб-страницы удаляется браузером.

Активность на самой странице можно обнаружить, перехватывая действия мыши и клавиатуры для всего тела страницы:

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