AJAX 请求期间异常的自定义处理导致 HttpException

Custom handling of exceptions during AJAX request is causing a HttpException

当我的应用程序在 AJAX 请求期间遇到类型 UnauthorizedAccessException 的异常时,我想自己处理该行为并 return 自定义 JSON 响应。

所以我覆盖了基本控制器中的 OnException 方法,我的所有控制器都继承自该方法,如下所示:

protected override void OnException(ExceptionContext filterContext)
{
    var exception = filterContext.Exception;

    if (exception is UnauthorizedAccessException)
    {
        filterContext.ExceptionHandled = true;

        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
            filterContext.HttpContext.Response.ContentType = "application/json";

            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string json = serializer.Serialize(new { IsUnauthenticated = true });
            filterContext.HttpContext.Response.Write(json);

            filterContext.HttpContext.Response.End();
        }
        else
        {
            filterContext.Result = RedirectToAction("LogOut", "Account");
        }
    }
    else
    {
        // Allow the exception to be processed as normal.
        base.OnException(filterContext);
    }
}

现在这几乎完全符合我的要求。如果在 AJAX 请求期间发生异常,我的 JavaScript 将根据需要取回正确的 JSON 对象。

但是,问题是应用程序随后在内部遭受 HttpException 消息:

Cannot redirect after HTTP headers have been sent.

以及来自异常的堆栈跟踪:

at System.Web.HttpResponse.Redirect(String url, Boolean endResponse, Boolean permanent) at System.Web.Security.FormsAuthenticationModule.OnLeave(Object source, EventArgs eventArgs) at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

我在 MvcApplicationApplication_Error 方法中添加断点时得到此异常信息,如下所示:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    LogError(ex);
}

因此,尽管我的应用程序在用户体验方面完全符合我的要求。我有这个 "behind the scenes" 异常,我真的不想发生。

这里出了什么问题?我该怎么做才能防止异常发生?

继续评论,由于字符数限制,我认为最好 post 将其作为答案。这部分是一个答案,因为我面前没有合适的项目来测试。

当我在评论中说我无法复制时,那是因为我 运行 我正在处理的项目中的那些测试是 WebApi2,我不会有相同的行为。

如果我没记错的话,您的问题在于您正在将 API 类功能实施到 MVC 项目中,当然您所看到的是预期的行为。

当您收到 UnauthorizedException 时,框架将尝试自动将您重定向到登录屏幕(或错误页面)。您需要禁用该行为(显然)。

您可以尝试使用以下方法在处理程序中抑制它:

filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
filterContext.HttpContext.Response.Redirect(null);

最终结果应该是这样的:

if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
    filterContext.HttpContext.Response.StatusCode = 
    (int)System.Net.HttpStatusCode.Unauthorized;

    filterContext.HttpContext.Response.ContentType = "application/json";



    filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;

    JavaScriptSerializer serializer = new JavaScriptSerializer();
    string json = serializer.Serialize(new { IsUnauthenticated = true });
    filterContext.HttpContext.Response.Write(json);

    filterContext.HttpContext.Response.End();
}

如果这不起作用;您的身份验证中间件也可能负责设置此重定向,不幸的是,这将在其他地方设置。

我的另一个答案是错误的,我刚测试过

问题的解决方案与我之前给你的解决方案略有不同。您的中间件是导致问题的原因。

我怀疑你用的是我用的; Microsoft.ASPNET.Identity.

在您的 Startup.cs 中,您需要添加 OnApplyRedirect 委托。

您的代码应该与我的类似:

app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
                     OnApplyRedirect = ctx =>
                     {
                         // add json response here...
                         ctx.RedirectUri = null;

                     }
                }
            });

从您的原始处理程序,移动响应并将其添加到 ctx.Response...

希望这能让它回到正轨。在 OnApplyRedirect 中,您可能需要检查它是否是 ajax 请求,然后禁用重定向,否则您将得到难看的 asp 默认错误页面。:-)