簡體   English   中英

獲取asp.net mvc 中的在線用戶列表

[英]get a list of online users in asp.net mvc

我的應用程序中有一個頁面,它總是顯示更新的在線用戶列表。 現在,為了使存儲在應用程序對象中的列表保持更新,我執行以下步驟

  1. 登錄時將用戶添加到列表

  2. 注銷時刪除用戶

  3. 然后為了處理瀏覽器關閉/導航離開的情況,我有一個時間戳以及集合中的用戶名每 90 秒一次 ajax 調用更新時間戳。

問題:我需要每 120 秒清理一次此列表以刪除帶有舊時間戳的條目。

如何在我的 Web 應用程序中執行此操作? 即每 2 分鍾調用一次函數。

PS:我想過使用調度程序每 2 分鍾調用一次 web 服務,但是托管環境不允許任何調度。

在全局過濾器中執行以下操作。

public class TrackLoginsFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Dictionary<string, DateTime> loggedInUsers = SecurityHelper.GetLoggedInUsers();

        if (HttpContext.Current.User.Identity.IsAuthenticated )
        {
            if (loggedInUsers.ContainsKey(HttpContext.Current.User.Identity.Name))
            {
                loggedInUsers[HttpContext.Current.User.Identity.Name] = System.DateTime.Now;
            }
            else
            {
                loggedInUsers.Add(HttpContext.Current.User.Identity.Name, System.DateTime.Now);
            }

        }

        // remove users where time exceeds session timeout
        var keys = loggedInUsers.Where(u => DateTime.Now.Subtract(u.Value).Minutes >
                   HttpContext.Current.Session.Timeout).Select(u => u.Key);
        foreach (var key in keys)
        {
            loggedInUsers.Remove(key);
        }

    }
}

檢索用戶列表

public static class SecurityHelper
{
    public static Dictionary<string, DateTime> GetLoggedInUsers()
    {
        Dictionary<string, DateTime> loggedInUsers = new Dictionary<string, DateTime>();

        if (HttpContext.Current != null)
        {
            loggedInUsers = (Dictionary<string, DateTime>)HttpContext.Current.Application["loggedinusers"];
            if (loggedInUsers == null)
            {
                loggedInUsers = new Dictionary<string, DateTime>();
                HttpContext.Current.Application["loggedinusers"] = loggedInUsers;
            }
        }
        return loggedInUsers;

    }
}

不要忘記在 global.asax 中注冊您的過濾器。 有一個應用程序設置來關閉它可能是個好主意。

GlobalFilters.Filters.Add(new TrackLoginsFilter());

還要在注銷時刪除用戶以更准確。

SecurityHelper.GetLoggedInUsers().Remove(WebSecurity.CurrentUserName);

在您的帳戶控制器中

   public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            if (Membership.ValidateUser(model.UserName, model.Password))
            {
                FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                if (HttpRuntime.Cache["LoggedInUsers"] != null) //if the list exists, add this user to it
                {
                    //get the list of logged in users from the cache
                    List<string> loggedInUsers = (List<string>)HttpRuntime.Cache["LoggedInUsers"];
                    //add this user to the list
                    loggedInUsers.Add(model.UserName);
                    //add the list back into the cache
                    HttpRuntime.Cache["LoggedInUsers"] = loggedInUsers;
                }
                else //the list does not exist so create it
                {
                    //create a new list
                    List<string> loggedInUsers = new List<string>();
                    //add this user to the list
                    loggedInUsers.Add(model.UserName);
                    //add the list into the cache
                    HttpRuntime.Cache["LoggedInUsers"] = loggedInUsers;
                }
                if (!String.IsNullOrEmpty(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                else
                {

                    return RedirectToAction("Index", "Home");
                }
            }
            else
            {
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }


    public ActionResult LogOff()
    {
        string username = User.Identity.Name; //get the users username who is logged in
        if (HttpRuntime.Cache["LoggedInUsers"] != null)//check if the list has been created
        {
            //the list is not null so we retrieve it from the cache
            List<string> loggedInUsers = (List<string>)HttpRuntime.Cache["LoggedInUsers"];
            if (loggedInUsers.Contains(username))//if the user is in the list
            {
                //then remove them
                loggedInUsers.Remove(username);
            }
            // else do nothing
        }
        //else do nothing
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }

在你的部分觀點中。

@if (HttpRuntime.Cache["LoggedInUsers"] != null)
{
    List<string> LoggedOnUsers = (List<string>)HttpRuntime.Cache["LoggedInUsers"];
    if (LoggedOnUsers.Count > 0)
    {
    <div class="ChatBox">
        <ul>
            @foreach (string user in LoggedOnUsers)
            {
                <li>
                    <div class="r_row">
                       <div class="r_name">@Html.Encode(user)</div>
                    </div>
                </li>
            }
        </ul>
    </div>
    }
}

當用戶登錄時呈現這個局部視圖。

每隔 90 秒使用此腳本調用

<script type="text/javascript">
    $(function () {
        setInterval(loginDisplay, 90000);
    });

    function loginDisplay() {
        $.post("/Account/getLoginUser", null, function (data) {

        });
    }
</script>

這是白象解決方案。

不是在應用程序對象中維護這個列表,而是在數據庫中維護這個列表。 然后您可以使用數據庫作業定期處理此列表。 在此對象上建立 SQL 通知,以便每次清除此列表時,您都會在應用程序中獲得刷新的數據。

使用 Ajax 每 30 秒向服務器發送“我仍然在線”消息。 這是找出誰真正在線的最佳方式。

我自己找到了解決方案。 如果有人要求,我會發布鏈接。

http://www.codeproject.com/KB/aspnet/ASPNETService.aspx

所以在這里我做了什么:

  1. 在數據庫中創建一個表

    CREATE TABLE [dbo].[OnlineUser] ( [ID] [int] IDENTITY(1,1) NOT NULL, [Guid] [uniqueidentifier] NOT NULL, [Email] [nvarchar](500) NOT NULL, [Created] [datetime] NOT NULL, CONSTRAINT [PK_OnlineUser] PRIMARY KEY CLUSTERED ( [ID] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
  2. 覆蓋 OnActionExecution 方法。 這個方法在一個單獨的控制器中,在我的例子中稱為 AuthController 然后每個其他需要身份驗證的控制器都從這個控制器繼承。

     protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); // session variable that is set when the user authenticates in the Login method var accessSession = Session[Constants.USER_SESSION]; // load cookie is set when the user authenticates in the Login method HttpCookie accessCookie = System.Web.HttpContext.Current.Request.Cookies[Constants.USER_COOKIE]; // create session from cookie if (accessSession == null) { if (accessCookie != null) { if (!string.IsNullOrEmpty(accessCookie.Value)) accessSession = CreateSessionFromCookie(accessCookie); } } // if session does not exist send user to login page if (accessSession == null) { filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { {"controller", "Account"}, {"action", "Login"} } ); return; } else { TrackLoggedInUser(accessSession.ToString()); } } private List<OnlineUser> TrackLoggedInUser(string email) { return GetOnlineUsers.Save(email); }
  3. 接下來我在 Data Repository 類中創建了以下類:GetOnlineUsers

     public static class GetOnlineUsers { public static List<OnlineUser> GetAll() { using (var db = new CEntities()) { return db.OnlineUsers.ToList(); } } public static OnlineUser Get(string email) { using (var db = new CEntities()) { return db.OnlineUsers.Where(x => x.Email == email).FirstOrDefault(); } } public static List<OnlineUser> Save(string email) { using (var db = new CEntities()) { var doesUserExist = db.OnlineUsers.Where(x => x.Email.ToLower() == email.ToLower()).FirstOrDefault(); if (doesUserExist != null) { doesUserExist.Created = DateTime.Now; db.SaveChanges(); } else { OnlineUser newUser = new OnlineUser(); newUser.Guid = Guid.NewGuid(); newUser.Email = email; newUser.Created = DateTime.Now; db.OnlineUsers.Add(newUser); db.SaveChanges(); } return GetAll(); } } public static void Delete(OnlineUser onlineUser) { using (var db = new CEntities()) { var doesUserExist = db.OnlineUsers.Where(x => x.Email.ToLower() == onlineUser.Email.ToLower()).FirstOrDefault(); if (doesUserExist != null) { db.OnlineUsers.Remove(doesUserExist); db.SaveChanges(); } } } }
  4. 在 Global.asax

     protected void Application_EndRequest() { // load all active users var loggedInUsers = GetOnlineUsers.GetAll(); // read cookie if (Context.Request.Cookies[Constants.USER_SESSION] != null) { // the cookie has the email string email = Context.Request.Cookies[Constants.USER_SESSION].ToString(); // send the user's email to the save method in the repository // notice in the save methos it also updates the time if the user already exist loggedInUsers = GetOnlineUsers.Save(email); } // lets see we want to clear the list for inactive users if (loggedInUsers != null) { foreach (var user in loggedInUsers) { // I am giving the user 10 minutes to interact with the site. // if the user interaction date and time is greater than 10 minutes, removing the user from the list of active user if (user.Created < DateTime.Now.AddMinutes(-10)) { GetOnlineUsers.Delete(user); } } } }
  5. 在繼承自 AuthController 的控制器之一(您可以創建一個新的控制器)中,創建以下方法:

     public JsonResult GetLastLoggedInUserDate() { string email = Session[Constants.USER_SESSION].ToString(); var user = GetOnlineUsers.Get(email); return Json(new { year = user.Created.Year, month = user.Created.Month, day = user.Created.Day, hours = user.Created.Hour, minutes = user.Created.Minute, seconds = user.Created.Second, milliseconds = user.Created.Millisecond }, JsonRequestBehavior.AllowGet); }
  6. 在 _Layout.cshtml 文件的最底部放置此 Javascript 代碼:此 Javascript 代碼將調用上面的 GetLastLoggedInUserDate() 以從數據庫中獲取上次交互日期。

     <script> var lastInteracted, DifferenceInMinutes; $(window).on('load', function (event) { $.get("get-last-interaction-date", function (data, status) { lastInteracted = new Date(data.year.toString() + "/" + data.month.toString() + "/" + data.day.toString() + " " + data.hours.toString() + ":" + data.minutes.toString() + ":" + data.seconds.toString()); }); }); $(window).on('mousemove', function (event) { var now = new Date(); DifferenceInMinutes = (now.getTime() - lastInteracted.getTime()) / 60000; if (DifferenceInMinutes > 5) { $.get("get-last-interaction-date", function (data, status) { lastInteracted = new Date(data.year.toString() + "/" + data.month.toString() + "/" + data.day.toString() + " " + data.hours.toString() + ":" + data.minutes.toString() + ":" + data.seconds.toString()); }); } }); </script>

JavaScript 解釋:

在頁面加載時,我正在設置用戶與我的網站交互的最后日期時間。

由於我無法跟蹤用戶在屏幕上注視的內容,因此最接近真實交互的是鼠標移動。 因此,當用戶將鼠標移動到頁面上的任意位置時,會發生以下情況:

  1. 我將上次交互日期與當前日期進行比較。
  2. 然后我檢查自上次更新日期發生以來是否過去了 5 分鍾。

由於用戶碰巧喜歡該網站並決定在該網站上花費更多時間,所以在 5 分鍾過去后,我向控制器GetLastLoggedInUserDate()的 this 方法發送另一個請求以再次獲取日期。 但是在我們獲得日期之前,我們將執行OnActionExecuting方法,然后該方法將更新記錄創建日期並返回當前時間。 lastInteracted獲取更新日期,然后我們再次訪問。

這里的想法是,當用戶沒有與我的網站進行交互時,他對我來說並不是真的在線。 也許他打開了 100 個標簽並玩游戲做其他事情,但與我的網站交互時,他們甚至可能不會意識到他們在幾天或幾個月內打開了它,這取決於他們重新啟動 PC 的頻率。 無論如何,我認為 10 分鍾是一個很好的門檻,但可以隨意更改。

最后是 AdminController 類:

    public ActionResult Index()
    {
        DashboardViewModel model = new DashboardViewModel();

        // loading the list of online users to the dashboard
        model.LoggedInUsers = GetOnlineUsers.GetAll();

        return View("Index", "~/Views/Shared/_adminLayout.cshtml", model);
    }

Index.cshtml(管理儀表板頁面)

@model ILOJC.Models.Admin.DashboardViewModel

@{
    ViewBag.Menu1 = "Dashboard";
}

/// some html element and styles

<h5 class="">@Model.LoggedInUsers.Count() Online Users</h5>     
<div class="row">
    @foreach (var user in Model.LoggedInUsers.OrderByDescending(x => x.Created))
    {       
       <div class="col-md-12">   
           <h5>@user.Email</h5>                                       
           <p><span>Last Inreaction Time: @user.Created.ToString("MM/dd/yyyy hh:mm:ss tt")</span></p>      
       </div>                                                              
    }
</div>

由於原始表只會存儲在線用戶,我想要一些歷史記錄/日志,因此我在數據庫中創建了一個歷史記錄表:

CREATE TABLE [dbo].[OnlineUserHistory](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [OnlineUserID] [int] NOT NULL,
    [Guid] [uniqueidentifier] NOT NULL,
    [Email] [nvarchar](500) NOT NULL,
    [Created] [datetime] NOT NULL,
    [Updated] [datetime] NOT NULL,
    [Operation] [char](3) NOT NULL,
 CONSTRAINT [PK_OnlineUserLog] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

最后,我在插入和刪除時創建了一個數據庫觸發器

CREATE TRIGGER [dbo].[trg_online_user_history]
ON [dbo].[OnlineUser]
AFTER INSERT, DELETE
AS
BEGIN
    SET NOCOUNT ON;
    INSERT INTO OnlineUserHistory(
        OnlineUserID, 
        [Guid],
        Email,
        Created,        
        Updated, 
        Operation
    )
    SELECT
        i.ID, 
        i.[Guid],
        i.Email,
        i.Created,        
        GETDATE(),
        'INS'
    FROM
        inserted i
    UNION ALL
    SELECT
        d.ID, 
        d.[Guid],
        d.Email,
        d.Created,
        GETDATE(),
        'DEL'
    FROM
       deleted d;
END

希望這可以幫助某人。 我要改進的一件事是在線用戶在儀表板中顯示負載的方式。 現在,我需要刷新頁面以查看更新后的數字。 但是,如果您想實時查看它,只需添加 SignalR 庫,然后創建一個集線器即可!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM