处理ajax调用中的会话超时

Jus*_*tin 56 asp.net-mvc jquery authorize-attribute asp.net-mvc-3

我正在使用jquery对asp.net mvc控制器动作进行ajax调用:

[AcceptVerbs(HttpVerbs.Post)]
        public ActionResult GetWeek(string startDay)
        {
            var daysOfWeek = CompanyUtility.GetWeek(User.Company.Id, startDay);
            return Json(daysOfWeek);
        }
Run Code Online (Sandbox Code Playgroud)

当会话超时时,此调用将失败,因为User对象存储在会话中.我创建了一个自定义authorize属性,以检查会话是否丢失并重定向到登录页面.这适用于页面请求,但它不适用于ajax请求,因为您无法从ajax请求重定向:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthorizeUserAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (!httpContext.Request.IsAjaxRequest())
            {//validate http request.
                if (!httpContext.Request.IsAuthenticated
                    || httpContext.Session["User"] == null)
                {
                    FormsAuthentication.SignOut();
                    httpContext.Response.Redirect("~/?returnurl=" + httpContext.Request.Url.ToString());
                    return false;
                }
            }
            return true;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我在另一个线程上读到,当用户未经过身份验证并且你发出ajax请求时,你应该将状态代码设置为401(未授权),然后在js中检查它并将它们重定向到登录页面.但是,我不能让这个工作:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (Request.IsAjaxRequest() && (!Request.IsAuthenticated || User == null))
            {
                filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
            }
            else
            {
                base.OnActionExecuting(filterContext);
            }
        }
Run Code Online (Sandbox Code Playgroud)

基本上,它将它设置为401,但随后它将继续进入控制器操作并抛出一个对象引用未设置为对象错误的实例,然后将错误500返回给客户端js.如果我更改我的自定义Authorize属性以验证ajax请求并为未经过身份验证的那些返回false,则会使ajax请求返回我的登录页面,这显然不起作用.

我该如何工作?

Dar*_*rov 85

您可以编写一个自定义[Authorize]属性,该属性将返回JSON,而不是在未经授权的访问时抛出401异常,这将允许客户端脚本优雅地处理该方案:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new JsonResult
            {
                Data = new 
                { 
                    // put whatever data you want which will be sent
                    // to the client
                    message = "sorry, but you were logged out" 
                },
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用它和客户端装饰你的控制器/动作:

$.get('@Url.Action("SomeAction")', function (result) {
    if (result.message) {
        alert(result.message);
    } else {
        // do whatever you were doing before with the results
    }
});
Run Code Online (Sandbox Code Playgroud)

  • Ooooo Darin Dimitrov.我爱你的帖子:-)每次它帮助我们.+1 (9认同)
  • 但这意味着所有客户端代码进行ajax调用(无论是使用jQuery还是通过Ajax.ActionLink,或Ajax.BeginForm)都应该算得上.这可以用于一个小项目,但随着网站的增长,维持这一点最终将成为一场噩梦. (8认同)
  • @Murali,当然,你可以注册一个全局的[`.ajaxComplete()`](http://api.jquery.com/ajaxComplete/)处理程序. (5认同)
  • Mosh,大多数客户端框架(如jQuery)都具有全局错误处理功能.例如,jQuery具有.ajaxError(),只要Ajax请求因错误而完成就会触发.这使得在客户端处理错误变得非常容易,即使对于最大的项目也是如此. (4认同)

fre*_*ide 37

我不会将JsonRequestBehavior更改为AllowGet.相反,我建议:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class MyAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        OnAuthorizationHelp(filterContext);
    }

    internal void OnAuthorizationHelp(AuthorizationContext filterContext)
    {

        if (filterContext.Result is HttpUnauthorizedResult)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.StatusCode = 401;
                filterContext.HttpContext.Response.End();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并添加全局js ajax错误处理程序:

   $(document).ajaxError(function (xhr, props) {
        if (props.status === 401) {
            location.reload(); 
        }
   }
Run Code Online (Sandbox Code Playgroud)

  • 对我来说这更好用,因为我使用的是一个我无法完全放入所有这些检查的ui框架.让全球客户端更好地抓住我的所有工作 (3认同)

Nic*_*oli 8

虽然这已经过去了,但我认为如果您使用的是.NET 4.5,这是最简短和最甜蜜的答案.添加了名为SuppressFormsAuthenticationRedirect的小属性.设置为true,它不会执行302重定向登录页面.

http://msdn.microsoft.com/en-us/library/system.web.httpresponse.suppressformsauthenticationredirect.aspx

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AjaxAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // returns a 401 already
        base.HandleUnauthorizedRequest(filterContext);
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            // we simply have to tell mvc not to redirect to login page
            filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

假设您计划处理ajax请求失败/错误回调,其中您将获得401 Unauthorized.


Sho*_*oeb 6

在Master页面上添加这个jquery脚本------------

<script type="text/javascript">

   $.ajaxSetup({
        statusCode: {
            403: function () {
                window.location.reload();
            }
        }
    });


    OR


    $.ajaxSetup({
        error: function (x, e) {
            if (x.status == 403) {
                window.location.reload(); 
            }
        }
    });

</script>
Run Code Online (Sandbox Code Playgroud)

在项目中添加一个以TraceFilter命名的cs文件,并编写一个继承到ActionFilterAttribute的封装类TraceFilterAttribute.通过写下面的行,在项目的App_Start文件夹中的FilterConfig.cs中添加TraceFilterAttribute类.

filters.Add(new TraceFilterAttribute());

在TraceFilterAttribute类中重写方法OnActionExecuting().这将自动检查会话,如果找到会话null,则调用母版页中可用的脚本,然后您可以转到您的选择页面.

[AttributeUsage(AttributeTargets.All)]
public sealed class TraceFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext != null)
        {
HttpSessionStateBase objHttpSessionStateBase = filterContext.HttpContext.Session;
                var userSession = objHttpSessionStateBase["etenetID"];
if (((userSession == null) && (!objHttpSessionStateBase.IsNewSession)) || (objHttpSessionStateBase.IsNewSession))
                {
                    objHttpSessionStateBase.RemoveAll();
                    objHttpSessionStateBase.Clear();
                    objHttpSessionStateBase.Abandon();
                    if (filterContext.HttpContext.Request.IsAjaxRequest())
                    {
                        filterContext.HttpContext.Response.StatusCode = 403;
                        filterContext.Result = new JsonResult { Data = "LogOut" };
                    }
                    else
                    {
                        filterContext.Result = new RedirectResult("~/Admin/GoToLogin");
                    }

                }


}
}

}
Run Code Online (Sandbox Code Playgroud)