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)
我在 MvcApplication
的 Application_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 默认错误页面。:-)
当我的应用程序在 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)
我在 MvcApplication
的 Application_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 默认错误页面。:-)