Giữ phiên ASP.NET mở / tồn tại


115

Cách dễ nhất và không bị xáo trộn nhất để giữ cho một phiên ASP.NET tồn tại miễn là người dùng mở cửa sổ trình duyệt? Các cuộc gọi AJAX có được hẹn giờ không? Tôi muốn ngăn chặn những điều sau: đôi khi người dùng giữ cửa sổ của họ mở trong một thời gian dài, sau đó nhập nội dung và khi gửi thì không có gì hoạt động nữa vì phiên phía máy chủ đã hết hạn. Tôi không muốn tăng giá trị thời gian chờ quá 10 phút trên máy chủ vì tôi muốn các phiên đã đóng (bằng cách đóng cửa sổ trình duyệt) hết nhanh.

Gợi ý, mẫu mã?


Bạn cũng có thể kiểm tra liên kết này để nhận câu trả lời dotnetcurry.com/ShowArticle.aspx?ID=453
Nhà phát triển

Câu trả lời:


170

Tôi sử dụng JQuery để thực hiện một lệnh gọi AJAX đơn giản đến một Trình xử lý HTTP giả không làm gì khác ngoài việc giữ cho Phiên của tôi tồn tại:

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

Trình xử lý phiên có thể đơn giản như sau:

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

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

Chìa khóa là thêm IRequiresSessionState, nếu không Session sẽ không khả dụng (= null). Tất nhiên, trình xử lý cũng có thể trả về một đối tượng được tuần tự hóa JSON nếu một số dữ liệu được trả về JavaScript đang gọi.

Được cung cấp thông qua web.config:

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

được thêm từ balexandre vào ngày 14 tháng 8 năm 2012

Tôi rất thích ví dụ này, đến nỗi tôi muốn cải thiện bằng HTML / CSS và phần beat

thay đổi điều này

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

thành

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

và thêm

// 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 và CSS

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

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

đây là một ví dụ trực tiếp chỉ cho phần đánh bại: http://jsbin.com/ibagob/1/


@veggerby "tới một Trình xử lý HTTP giả không làm gì khác ngoài việc giữ cho Phiên của tôi tồn tại". Bạn có thể vui lòng đăng mã mẫu của Trình xử lý HTTP để giữ cho phiên hoạt động không?
Gopinath

không, và lựa chọn duy nhất của bạn đó (tôi đoán) là để tăng thời gian chờ phiên, nhưng đó có lẽ là một ý tưởng tồi trong thời gian dài
veggerby

Đang thực hiện các cuộc điều tra về vấn đề liên quan và đã tìm ra giải pháp này. Đồ tốt. Tuy nhiên, một truy vấn, nếu người dùng để trình duyệt của họ mở và nói rằng máy tính không chuyển sang chế độ ngủ trong 10 giờ, phiên sẽ được duy trì lâu như vậy, phải không? Thê nay đung không?
Julius A

2
Công việc tốt nhưng nó không thành công với tôi cho đến khi tôi thêm một cụm bộ nhớ cache vào cuộc gọi. Nếu không có tham số bùng nổ bộ nhớ cache này, bộ điều khiển được gọi ngay lần đầu tiên
jean

1
@stom nó có nghĩa là một giá trị phiên, không phải thời gian chờ hoặc bất cứ điều gì. Lý do sử dụng DateTime.Nowchỉ là để làm cho nó rõ ràng khi phiên được cập nhật lần cuối qua nhịp tim.
veggerby

69

Nếu bạn đang sử dụng ASP.NET MVC - bạn không cần trình xử lý HTTP bổ sung và một số sửa đổi của tệp web.config. Tất cả những gì bạn cần - chỉ để thêm một số hành động đơn giản trong Bộ điều khiển gia đình / Chung:

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

, hãy viết một đoạn mã JavaScript như đoạn mã này (tôi đã đặt nó vào một trong các tệp JavaScript của trang web):

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

và khởi tạo chức năng này bằng cách gọi một hàm JavaScript:

SetupSessionUpdater('/Home/KeepSessionAlive');

Xin lưu ý! Tôi đã triển khai chức năng này chỉ cho người dùng được ủy quyền (không có lý do gì để giữ trạng thái phiên cho khách trong hầu hết các trường hợp) và quyết định giữ trạng thái phiên hoạt động không chỉ dựa trên - trình duyệt có mở hay không, mà người dùng được ủy quyền phải thực hiện một số hoạt động trên trang web (di chuyển chuột hoặc nhập một số phím).


3
Đối với MVC, tôi nghĩ đây là câu trả lời tốt hơn. Không cần sử dụng tệp .ashx, tại sao bạn sẽ?
arame3333

Với HTTP Handler trong asp mvc kiểm tra điều này , hy vọng sẽ giúp ai đó.
shaijut

3
Maryan, bạn cũng có thể muốn sử dụng @Url.Action("KeepSessionAlive","Home")trong hàm khởi tạo để bạn không phải mã hóa URL và cũng có thể ném khối đầu tiên vào bên trong IIFE và chỉ xuất khối SetupSessionUpdatervì đó là thứ duy nhất cần được gọi ra bên ngoài - đại loại là cái này: SessionUpdater.js
KyleMit,

Có lý do gì khiến setInterval không được sử dụng không? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier

8

Bất cứ khi nào bạn yêu cầu máy chủ, thời gian chờ của phiên sẽ đặt lại. Vì vậy, bạn chỉ có thể thực hiện một cuộc gọi ajax tới một trình xử lý HTTP trống trên máy chủ, nhưng hãy đảm bảo rằng bộ đệm ẩn của trình xử lý bị vô hiệu hóa, nếu không trình duyệt sẽ lưu vào bộ nhớ cache của trình xử lý của bạn và không đưa ra yêu cầu mới.

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 - Không cần chi phí lưu trữ các biến trong phiên. Chỉ cần định dạng trước một yêu cầu đến máy chủ là đủ.


2

Bạn có thực sự cần giữ phiên (bạn có dữ liệu trong đó không?) Hay là đủ để giả mạo điều này bằng cách khôi phục phiên khi có yêu cầu? Nếu là người đầu tiên, hãy sử dụng phương pháp trên. Nếu thứ hai, hãy thử một cái gì đó như sử dụng trình xử lý sự kiện Session_End.

Nếu bạn có Xác thực Mẫu, thì bạn sẽ nhận được một cái gì đó trong Global.asax.cs như

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

Và bạn đặt thời gian tồn tại của vé lâu hơn nhiều so với thời hạn của phiên. Nếu bạn không xác thực hoặc sử dụng một phương pháp xác thực khác, có những thủ thuật tương tự. Giao diện web TFS của Microsoft và SharePoint dường như sử dụng những thứ này - điều cho đi là nếu bạn nhấp vào một liên kết trên một trang cũ, bạn sẽ nhận được lời nhắc xác thực trong cửa sổ bật lên, nhưng nếu bạn chỉ sử dụng một lệnh, nó sẽ hoạt động.


2

bạn chỉ có thể viết mã này trong tệp kịch bản java của bạn.

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

Đây (20-1)*60*1000là thời gian làm mới, nó sẽ làm mới thời gian chờ của phiên. Thời gian chờ làm mới được tính theo thời gian mặc định ngoài iis = 20 phút, nghĩa là 20 × 60000 = 1200000 mili giây - 60000 mili giây (Một phút trước khi phiên hết hạn) là 1140000.


0

Đây là một giải pháp thay thế sẽ tồn tại nếu máy khách chuyển sang chế độ ngủ.

Nếu bạn có một lượng lớn người dùng đã đăng nhập thì hãy sử dụng nó một cách thận trọng vì điều này có thể ăn nhiều bộ nhớ máy chủ.

Sau khi bạn đăng nhập (tôi thực hiện việc này trong sự kiện LoggedIn của kiểm soát đăng nhập)

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)

Tại sao điều này lại bị bỏ phiếu? có một số loại vấn đề với nó mà tôi chưa đề cập đến? nếu đúng như vậy hãy chia sẻ để những độc giả sau này được biết nhé!
Peter

0

Tôi đã dành vài ngày để cố gắng tìm ra cách kéo dài phiên người dùng trong WebForms thông qua hộp thoại bật lên cung cấp cho người dùng tùy chọn gia hạn phiên hoặc cho phép phiên đó hết hạn. Điều số 1 mà bạn cần biết là bạn không cần bất kỳ nội dung 'HttpContext' cầu kỳ nào trong một số câu trả lời khác. Tất cả những gì bạn cần là $ .post () của jQuery; phương pháp. Ví dụ, trong khi gỡ lỗi, tôi đã sử dụng:

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

và trên trang trực tiếp của bạn, bạn sẽ sử dụng một cái gì đó như:

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

Nó là dễ dàng như vậy. Hơn nữa, nếu bạn muốn nhắc người dùng có tùy chọn gia hạn phiên của họ, hãy làm điều gì đó tương tự như sau:

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

Đừng quên thêm Hộp thoại vào html của bạn:

        <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

Liên quan đến giải pháp của veggerby, nếu bạn đang cố gắng triển khai nó trên ứng dụng VB, hãy cẩn thận khi cố gắng chạy mã được cung cấp thông qua trình dịch. Những điều sau sẽ hoạt động:

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

Ngoài ra, thay vì gọi hàm like heartbeat () như:

 setTimeout("heartbeat()", 300000);

Thay vào đó, hãy gọi nó như:

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

Thứ nhất, setTimeout chỉ kích hoạt một lần trong khi setInterval sẽ kích hoạt nhiều lần. Thứ hai, gọi heartbeat () như một chuỗi không hoạt động với tôi, trong khi gọi nó như một hàm thực tế.

Và tôi hoàn toàn có thể khẳng định 100% rằng giải pháp này sẽ khắc phục được quyết định vô lý của GoDaddy khi buộc một phiên apppool dài 5 phút trong Plesk!


0

Đây là phiên bản plugin JQuery của giải pháp Maryan với tối ưu hóa tay cầm. Chỉ với 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)

và cách nó nhập vào * .cshtml của bạn

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

0

[Đến muộn ...]

Một cách khác để thực hiện việc này mà không cần gọi Ajax hoặc trình xử lý WebService là tải một trang ASPX đặc biệt sau một khoảng thời gian nhất định (tức là trước khi hết thời gian chờ trạng thái phiên, thường là 20 phút):

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

Các KeepAlive.aspxtrang chỉ đơn giản là một trang trắng tinh mà không làm gì nhưng touch / làm mới Sessionnhà nước:

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

Điều này hoạt động bằng cách tạo một imgphần tử (hình ảnh) và buộc trình duyệt tải nội dung của nó từ KeepAlive.aspxtrang. Việc tải trang đó khiến máy chủ chạm vào (cập nhật) Sessionđối tượng, kéo dài thời gian trượt thời gian hết hạn của phiên (thường thêm 20 phút). Nội dung trang web thực tế sẽ bị trình duyệt loại bỏ.

Một cách thay thế, và có lẽ rõ ràng hơn, để làm điều này là tạo một iframephần tử mới và tải KeepAlive.aspxtrang vào đó. Phần iframetử bị ẩn, chẳng hạn như bằng cách đặt nó thành phần tử con của divphần tử ẩn ở đâu đó trên trang.

Bản thân hoạt động trên trang có thể được phát hiện bằng cách chặn các tác vụ chuột và bàn phím cho toàn bộ nội dung trang:

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

Tôi không thể ghi công cho ý tưởng này; xem: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.