如何在asp.net mvc 2应用程序中自动保存应用程序时强制会话超时或用户注销?

How do I force session timeout or a logout of a user when the app auto saves in an asp.net mvc 2 application?

本文关键字:应用程序 会话 超时 用户注销 保存 asp net mvc      更新时间:2023-09-26

我看到有人问过这个问题,解决方案通常适用于其他语言,并不适用于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"