Custom handling of exceptions during AJAX request is causing a HttpException

核能气质少年 提交于 2020-01-06 09:03:51

问题


When my application encounters an exception of type UnauthorizedAccessException during an AJAX request, I want to handle the behaviour myself and return a custom JSON response.

So I have overridden the OnException method in a base controller, which all my conrtollers inherit from, like this:

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

Now this pretty much does exactly what I want. If the exception occurs during an AJAX request, my JavaScript will get the correct JSON object back as desired.

However, the problem is that the application then suffers a HttpException internally, with message:

Cannot redirect after HTTP headers have been sent.

And the stack trace from the exception:

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)

I get this exception information when adding a breakpoint to the Application_Error method of MvcApplication like this:

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

So although my application is producing the result exactly as I want in terms of user experience. I have this "behind the scenes" exception that I really don't want to happen.

What is going wrong here? And what can I do to prevent the exception from occurring?


回答1:


Continuing from the comments, I figured it's best to post it as an answer due to character limit. This is partly an answer as I don't have a right project in-front of me to test.

When I said in the comment that I can't replicate, that was because I ran those tests in the project I'm working on which is WebApi2 and I won't have the same behavior.

If my mind serves me right, your problem lies in the fact that you're implementing API like functionality into MVC project and of course what you're seeing is the expected behavior.

When you get UnauthorizedException the framework will try to redirect you to the login screen automatically (or an error page). You need to disable that behavior (obviously).

What you can try is supressing it in the handler using the below:

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

End result should be something along the lines of:

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

If this does not work; your authentication middleware could be also responsible for setting this redirect and that would be set elsewhere unfortunately.




回答2:


My other answer was wrong, I have just tested it.

The solution to the problem is slightly different to what previous solution I have given you. Your middleware is the one causing the issue.

I suspect you're using what I'm using; Microsoft.ASPNET.Identity.

Inside your Startup.cs, you need to add OnApplyRedirect delegate.

Your code should look similar to mine:

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;

                     }
                }
            });

From your original handler, move the response and add it to ctx.Response...

Hopefully this will put it back on track. Inside OnApplyRedirect you might want to check if it's ajax request then disable the redirect otherwise you'll get the ugly asp default error pages.. :-)



来源:https://stackoverflow.com/questions/51103611/custom-handling-of-exceptions-during-ajax-request-is-causing-a-httpexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!