Підтримування ASP.NET сесії відкритою / живою


115

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

Пропозиції, зразки коду?


Ви можете перевірити це посилання, щоб отримати відповідь також dotnetcurry.com/ShowArticle.aspx?ID=453
Розробник

Відповіді:


170

Я використовую 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 та 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 Handler, щоб зберегти сеанс живим?
Gopinath

ні, і ваш єдиний варіант (мабуть) - збільшити час очікування на сеанс, але це, мабуть, погана ідея в перспективі
veggerby

Проводив розслідування пов'язаних питань і натрапив на це рішення. Хороший матеріал. Один запит, якщо користувач залишить браузер відкритим і скаже, що ПК не лягає спати протягом 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

За допомогою HTTP Handler в asp mvc перевірте це , сподіваюся, що хтось допоможе.
shaijut

3
Мар'яне, ви також можете використовувати @Url.Action("KeepSessionAlive","Home")функцію ініціалізатора, тому вам не доведеться жорстко кодувати URL, а також кидати перший блок всередині IIFE і просто експортувати, SetupSessionUpdaterоскільки це єдине, що потрібно викликати зовні - щось на зразок це: SessionUpdater.js
KyleMit

Чи є причина, чому setInterval не використовується? setInterval(KeepSessionAlive, 300000)
Матьє Корм'є

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 - немає необхідності в накладних витратах на зберігання змінних в сеансі. Достатньо лише попередньої підготовки запиту до сервера.


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 thats it.

$(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 через спливаюче діалогове вікно, що дає користувачеві можливість поновити сеанс або дозволити його закінчення. Те, що вам потрібно знати, - це те, що вам не знадобиться жоден з цих фантазійних матеріалів «HttpContext», який відбувається в деяких інших відповідях. Все, що вам потрібно, це $ .post (); jQuery; метод. Наприклад, під час налагодження я використовував:

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

Що стосується рішення Веггербі, якщо ви намагаєтесь реалізувати його в додатку 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 буде спрацьовувати кілька разів. Номер два, викликати серцебиття () як рядок для мене не працювало, тоді як викликати його як фактичну функцію.

І я можу абсолютно на 100% підтвердити, що це рішення подолає смішне рішення GoDaddy змусити 5-хвилинний сеанс роботи в Плеску!


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

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 .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.