如何在asp.net mvc 2应用程序中自动保存应用程序时强制会话超时或用户注销?
本文关键字:应用程序 会话 超时 用户注销 保存 asp net mvc | 更新日期: 2023-09-27 18:05:24
我看到有人问过这个问题,解决方案通常适用于其他语言,并不适用于ASP。. NET MVC 2.
我正在使用Jquery &Jquery表单以设定的间隔自动保存用户数据。我仍然希望应用程序能够超时,但通过jquery表单的自动保存会不断刷新服务器。
我最初解决这个问题的想法很简单。我已经有一个ActionFilter,我用它来查看会话是否过期。这个会话永远不会过期;但是,我只是根据会话中的值跟踪自动保存的发生次数,当它达到限制(在web.config中指定)时,它会执行:
filterContext.Result = new RedirectResult("~/Account.aspx/LogOn");
好吧,这不起作用,因为自动保存是做ajaxFormSubmit首先调用的动作。我试过改变重定向到登录页面的动作,但同样的事情发生....它只是不做重定向。该操作唯一可以返回的是Json结果。在我的最新版本(下面的代码)中,我将json返回值设置为false,并调用redirectToLogin()函数将页面发送到登录页面。它不工作,我不知道为什么。
对此有什么想法会很有帮助的。
在视图上设置自动保存间隔的代码节选(就在窗体关闭之前):
<%
double sessionTimeoutInMinutes = double.Parse(ConfigurationManager.AppSettings["SESSION_TIMEOUT_IN_MINUTES"].ToString());
double maxContiguousAutoSaves = double.Parse(ConfigurationManager.AppSettings["MAX_CONTIGUOUS_AUTO_SAVES"].ToString());
double autoSaveInterval = (sessionTimeoutInMinutes / maxContiguousAutoSaves) * 60 * 1000;
%>
<%= Html.Hidden("autoSaveInterval", autoSaveInterval) %>
<script type="text/javascript">
$(document).ready(function() {
var autoSaveFrequency = $('[id=autoSaveInterval]').val();
//alert(' Auto Save Interval in miliseconds: ' + autoSaveFrequency);
setInterval(
"initAutoSave('AutoSaveGoals', 'message')"
, autoSaveFrequency);
});
</script>
"AutoSaveGoals"目标是我的一个操作的名称。它处理post,更新会话中的某些项,并调用repository.update。定义如下:
[HttpPost]
public ActionResult AutoSaveGoals(Data data)
{
Data sessdata = Data();
sessdata.MpaGoals = data.Goals;
sessdata.MpaStatus = data.MpaStatus;
sessdata.StartPeriodDate = data.StartPeriodDate;
sessdata.EndPeriodDate = data.EndPeriodDate;
sessdata.AssociatePassword = data.AssociatePassword;
try
{
_repository.update(sessdata);
}
catch (Exception e)
{
LogUtil.Write("AutoSaveGoals", "Auto Save Goals Failed");
LogUtil.WriteException(e);
}
if (!autoLogOffUser(RouteData.GetRequiredString("action")))
return Json(new { success = true });
else
return Json(new { success = false });
}
initAutoSave函数是使用Jquery的javascript;Jquery表单插件。
function initAutoSave(targetUrl, messageDivId) {
var options = {
url: targetUrl,
type: 'POST',
beforeSubmit: showRequest,
success: function(data, textStatus) {
//alert('Returned from save! data: ' + data);
if (data.success) {
var currDateAndTime = " Page last saved on: " + getCurrentDateAndTime();
$('[id=' + messageDivId + ']').text(currDateAndTime).show('normal', function() { })
}
else {
alert('redirecting to login page');
redirectToLogin();
//$('[id=' + messageDivId + ']').text(' An error occurred while attempting to auto save this page.').show('normal', function() { })
//alert('ERROR: Page was not auto-saved properly!!!!');
}
}
};
$('form').ajaxSubmit(options);
}
我试着在redirectToLogin()中做一个javascript重定向,但它似乎没有得到url或幕后的东西正在爆炸。下面是它的定义:
function redirectToLogin() {
window.location = "Account.aspx/LogOn";
}
解决这个问题的最好方法是让你的代码总是返回一个Json结果,我使用一个名为StandardAjaxResponse的模型,它有一个ID,一个消息和一个答案答案总是假的,除非我的代码以正确的方式完成并将其设置为真。来自try/catch的任何错误都被放置到消息字段中,因此if !回答和留言等于没有登录,你可以再定位。Href到登录页面,而不获取登录页面作为ajax响应。
例如:public class AjaxGenericResponse{
public bool Answer {get;set; }
public int Id {ge; set; } // this is for cases when i want an ID result
public string Mesage {get;set;} // this is so i can show errors from ajax
}
控制器/动作
public JsonResult DoAutoSave(Data data){
var JsonResults = new AjaxGenericResponse{Answer=false};
// do code here to save etc
// no matter what always return a result, even if code is broken
return Json(model);
}
Javascript:
$.ajax({
url:"",
dataTYpe: 'json',
success:function(data){
if(data.Answer) {
// all is good
} else {
if(data.Message === "logout') { href.location=('login'); } else { alert(data.Message); }
}
}
});
无论如何这是一个解决方案!
我真笨。谢谢你的回复,但我认为我们的解决方案与答案一致。我的问题是我没有正确的url重定向到redirectToLogin方法。我做了一些小的调整,很快,它的重定向。
Javascript更改:
function redirectToLogin(url) {
window.location = url;
}
function initAutoSave(targetUrl, messageDivId) {
var options = {
url: targetUrl,
type: 'POST',
beforeSubmit: showRequest,
success: function(data, textStatus) {
//alert('Returned from save! data: ' + data);
if (data.success) {
var currDateAndTime = " Page last saved on: " + getCurrentDateAndTime();
$('[id=' + messageDivId + ']').text(currDateAndTime).show('normal', function() { })
}
else {
alert('redirecting to login page');
redirectToLogin(data.url);
//$('[id=' + messageDivId + ']').text(' An error occurred while attempting to auto save this page.').show('normal', function() { })
//alert('ERROR: Page was not auto-saved properly!!!!');
}
}
};
$('form').ajaxSubmit(options);
}
动作变化
if (!shouldAutoLogOffUser(RouteData.GetRequiredString("action")))
return Json(new { success = true, url = "" });
else
return Json(new { success = false , url = Url.Action("LogOff","Account").ToString() });
shouldAutoLogOffUser检查由动作过滤器更新的会话变量,以跟踪连续自动保存的#,并处理逻辑,以查看该值是否超过了允许的连续自动保存的最大#。动作过滤器检查了"AutoSave"的动作名,如果找到了,计数器就增加。否则,计数器被重置为0(发生了非自动保存的帖子)。
再来一个随机问题。如果这个应用程序加载在IFrame和窗口中。location调用时,IFrame内容会被更改还是整个页面(本质上是容器)会被更改?我们公司正在寻求运行一些我们的asp.net mvc 2应用程序在IFrame的通过websphere门户(是的,我知道....这不是我的选择)。
这太荒谬了…所以,我在看我的应用程序(我有几个要去QA很快),并注意到我已经解决了这个问题与一个更好的解决方案-它是在一个ActionFilter全部处理。当我问这个问题时,我想从一开始就这样做,但是已经实现了它,忘记了这一点,并再次询问堆栈溢出…我希望我的记忆问题能帮到你。下面是完整的动作过滤器代码。一如既往,我愿意接受批评,所以嘲笑它,修改它,复制它,等等。
public class UserStillActiveAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
int sessionTimeoutInMinutes = int.Parse(ConfigurationManager.AppSettings["SESSION_TIMEOUT"].ToString());
int maxContiguousAutoSaves = int.Parse(ConfigurationManager.AppSettings["MAX_CONSEC_SAVES"].ToString());
int autoSaveIntervalInMinutes = int.Parse(ConfigurationManager.AppSettings["AUTO_SAVE_INTERVAL"].ToString());
string actionName = filterContext.ActionDescriptor.ActionName;
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
HttpContext currentSession = HttpContext.Current;
LogAssociateGoalsSessionStatus(filterContext.HttpContext, actionName);
if (actionName.ToLower().Contains("autosave"))
{
int autoSaveCount = GetContigousAutoSaves(filterContext.HttpContext);
if (autoSaveCount == maxContiguousAutoSaves)
{
var result = new RedirectResult("~/Account.aspx/LogOff");
if (result != null && filterContext.HttpContext.Request.IsAjaxRequest())
{
//Value checked on Logon.aspx page and message displayed if not null
filterContext.Controller.TempData.Add(PersistenceKeys.SessionTimeOutMessage,
StaticData.MessageSessionExpiredWorkStillSaved);
string destinationUrl = UrlHelper.GenerateContentUrl(
result.Url,
filterContext.HttpContext);
filterContext.Result = new JavaScriptResult()
{
Script = "window.location='" + destinationUrl + "';"
};
}
}
else
{
RefreshContiguousAutoSaves(filterContext.HttpContext, autoSaveCount + 1);
}
}
else
{
RefreshContiguousAutoSaves(filterContext.HttpContext, 1);
}
}
private int GetContigousAutoSaves(HttpContextBase context)
{
Object o = context.Session[PersistenceKeys.ContiguousAutoUpdateCount];
int contiguousAutoSaves = 1;
if (o != null && int.TryParse(o.ToString(), out contiguousAutoSaves))
{
return contiguousAutoSaves;
}
else
{
return 1;
}
}
private void RefreshContiguousAutoSaves(HttpContextBase context,
int autoSavecount)
{
context.Session.Remove(PersistenceKeys.ContiguousAutoUpdateCount);
context.Session.Add(PersistenceKeys.ContiguousAutoUpdateCount,
autoSavecount);
}
private void LogAssociateGoalsSessionStatus(HttpContextBase filterContext, string actionName)
{
AssociateGoals ag = (AssociateGoals)filterContext.Session[(PersistenceKeys.SelectedAssociateGoals)];
bool assocGoalsIsNull = false;
bool assocGoalsInformationIsNull = false;
if (ag == null)
{
assocGoalsIsNull = true;
assocGoalsInformationIsNull = true;
}
else if (ag != null && ag.AssociateInformation == null)
assocGoalsInformationIsNull = true;
}
}
在Java脚本和jquery中总是使用双引号,以避免浏览器特有的问题
dataTYpe: 'json'必须为"dataTYpe:"json"