[英]get a list of online users in asp.net mvc
我的應用程序中有一個頁面,它總是顯示更新的在線用戶列表。 現在,為了使存儲在應用程序對象中的列表保持更新,我執行以下步驟
登錄時將用戶添加到列表
注銷時刪除用戶
然后為了處理瀏覽器關閉/導航離開的情況,我有一個時間戳以及集合中的用戶名每 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 秒向服務器發送“我仍然在線”消息。 這是找出誰真正在線的最佳方式。
我自己找到了解決方案。 如果有人要求,我會發布鏈接。
所以在這里我做了什么:
在數據庫中創建一個表
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]
覆蓋 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); }
接下來我在 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(); } } } }
在 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); } } } }
在繼承自 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); }
在 _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 解釋:
在頁面加載時,我正在設置用戶與我的網站交互的最后日期時間。
由於我無法跟蹤用戶在屏幕上注視的內容,因此最接近真實交互的是鼠標移動。 因此,當用戶將鼠標移動到頁面上的任意位置時,會發生以下情況:
由於用戶碰巧喜歡該網站並決定在該網站上花費更多時間,所以在 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.