【问题标题】: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 2 应用程序中时,如何强制会话超时或注销用户?
【发布时间】:2011-08-11 21:55:56
【问题描述】:

我已经看到这个问题被问了几种方法,解决方案通常适用于其他语言,不适用于 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”目标是我的一项操作的名称。它处理帖子,更新会话中的某些项目,并调用 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 & Jquery Forms 插件的 javascript。这里是:

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

【问题讨论】:

    标签: c# javascript jquery .net asp.net-mvc


    【解决方案1】:

    解决此问题的最佳方法是让您的代码始终返回 Json 结果,我使用名为 StandardAjaxResponse 的模型,该模型具有 ID、消息和答案答案始终为假,除非我的代码以正确的方式完成并设置它为真。来自 try / catch 的任何错误都会放入消息字段中,因此如果 !data.Answer 并且消息等于未登录,则您可以将 location.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); }
             }
    
        }
    });
    

    反正这是一种解决方案!

    【讨论】:

      【解决方案2】:

      我太傻了。感谢您的回复减去,但我认为我们的解决方案与答案一致。我的问题是我没有正确的 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 检查由操作过滤器更新的会话变量,以跟踪连续自动保存的次数并处理逻辑以查看该值是否超过了允许的最大连续自动保存次数。动作过滤器检查动作名称是否为“自动保存”,如果找到,计数器会增加。否则,计数器将重置为 0(发生非自动保存帖子)。

      另外一个随机问题。如果在 IFrame 中加载此应用程序并进行了 window.location 调用,是否会更改 IFrame 内容或更改整个页面(本质上是容器)?我们公司希望通过 websphere 门户在 IFrame 中运行我们的一些 asp.net mvc 2 应用程序(是的,我知道....这不是我的选择)。

      【讨论】:

      • 是的,伙计,一天结束时,它会根据需要扩展该模型,但始终交付它,因此避免在 iframe 和 iframe 中出现登录页面或错误页面的问题.window.loaction.href 如果我没记错的话 10 年内没有使用过 iframe :-))
      【解决方案3】:

      现在这简直太荒谬了...所以,我正在查看我的应用程序(我很快就有几个要去 QA)并指出我已经用一个更好的解决方案解决了这个问题 - 全部在 ActionFilter 中处理。当我问这个问题时,我从一开始就想要这个,但是已经实现了它,忘记了这一点,并在 Stack Overflow 上再次询问......嗯,我希望我的记忆问题能帮助某人解决这个问题。下面是完整的操作过滤器代码。与往常一样,我对批评持开放态度,因此请嘲笑、修改、复制等等。

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

      【讨论】:

        【解决方案4】:

        始终在 java 脚本和 jquery 中使用双引号以避免浏览器特定问题

        喜欢

        dataTYPE: 'json' 必须为 "dataTYPE:"json"

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-02
          • 1970-01-01
          • 2014-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多