Сохранение сеанса ASP.NET открытым / живым

116

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

Предложения, образцы кода?

Alex
источник
Вы также можете проверить эту ссылку, чтобы получить ответ. Dotnetcurry.com/ShowArticle.aspx?ID=453
Разработчик

Ответы:

171

Я использую 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, иначе сеанс не будет доступен (= null). Обработчик, конечно, также может вернуть сериализованный объект 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'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
источник
@veggerby "фиктивному обработчику HTTP, который ничего не делает, но поддерживает мой сеанс в живых". Не могли бы вы опубликовать образец кода HTTP-обработчика, чтобы поддерживать сеанс в рабочем состоянии?
Gopinath
нет, и ваш единственный вариант (я думаю) - увеличить тайм-аут сеанса, но, вероятно, это плохая идея в долгосрочной перспективе
veggerby
Проводил расследования по связанной проблеме и наткнулся на это решение. Хорошая вещь. Однако есть один вопрос: если пользователь оставляет свой браузер открытым и говорит, что компьютер не переходит в спящий режим в течение 10 часов, сеанс будет оставаться в живых так долго, верно? Это правильно?
Julius A
2
Хорошая работа, но мне это не удалось, пока я не добавил к звонку всплеск кеша. Без этого параметра всплеска кеша контроллер вызывается только в первый раз
jean
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
С обработчиком HTTP в asp mvc проверьте это , надежда кому-то поможет.
Shaijut
3
Мариан, вы также можете использовать @Url.Action("KeepSessionAlive","Home")функцию инициализатора, чтобы вам не приходилось жестко кодировать URL-адрес, а также вставлять первый блок внутри IIFE и просто экспортировать, SetupSessionUpdaterпоскольку это единственное, что нужно вызывать извне - что-то вроде это: SessionUpdater.js
KyleMit
Есть ли причина, по которой setInterval не используется? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier
8

Каждый раз, когда вы делаете запрос к серверу, тайм-аут сеанса сбрасывается. Таким образом, вы можете просто выполнить 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 - нет необходимости в хранении переменных в сеансе. Достаточно просто выполнить запрос к серверу.

BornToCode
источник
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 и 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

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

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

После входа в систему (я делаю это в событии 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 с помощью всплывающего диалогового окна, дающего пользователю возможность возобновить сеанс или позволить ему истечь. Вещь №1, которую вам нужно знать, - это то, что вам не нужен какой-либо из этих причудливых вещей «HttpContext», происходящих в некоторых других ответах. Все, что вам нужно, это jQuery's $ .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>
Colin
источник
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 о принудительном 5-минутном сеансе пула приложений в Plesk!

ShowJX1990
источник
0

Здесь версия плагина 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
AlexPalla
источник
0

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

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

Альтернативный и, возможно, более чистый способ сделать это - создать новый 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 .

Дэвид Р. Триббл
источник