更改 ajax 请求的 response.statuscode 会引发错误“发送 HTTP 标头后服务器无法设置状态”

Posted

技术标签:

【中文标题】更改 ajax 请求的 response.statuscode 会引发错误“发送 HTTP 标头后服务器无法设置状态”【英文标题】:Changing response.statuscode for ajax requests raise error "Server cannot set status after HTTP headers have been sent" 【发布时间】:2015-06-13 08:58:50 【问题描述】:

我在一个 MVC 项目中工作,其中每个操作方法的用户角色和权限都存储在数据库中。我想要实现的是通过覆盖 AuthorizeAttribute 来使用 mvc 中的授权过滤器功能。这样我就可以全局注册我的自定义授权过滤器。

这是我正在尝试做的事情,遵循this method。我正在关注this article 来处理ajax 请求。

protected override bool AuthorizeCore(HttpContextBase httpContext)

     //Custom permission checking logic 
     return false;//no permission for all methods-Just for testing


public override void OnAuthorization(AuthorizationContext filterContext)

    base.OnAuthorization(filterContext);
    CheckIfUserIsAuthenticated(filterContext);


private void CheckIfUserIsAuthenticated(AuthorizationContext filterContext)

    // If Result is null, we’re OK: the user is authenticated and authorized. 
    if (filterContext.Result == null)
        return;

    var httpContext = filterContext.HttpContext;
    var request = httpContext.Request;
    var response = httpContext.Response;

    var user = httpContext.User;
    // If here, you’re getting an HTTP 401 status code. In particular,
    // filterContext.Result is of HttpUnauthorizedResult type. Check Ajax here. 

    if (request.IsAjaxRequest())
    
        if (user.Identity.IsAuthenticated == false)
            response.StatusCode = (int)HttpStatusCode.Unauthorized;//401
        else
            response.StatusCode = (int)HttpStatusCode.Forbidden;//403

        response.SuppressFormsAuthenticationRedirect = true;

        response.End();// This line is causing the problems

        return;
    

    if (user.Identity.IsAuthenticated)
    
        //Redirect to customerror page
    

问题是当我使用 ajax post 调用方法时,我的自定义异常过滤器捕捉到错误提示 "Server cannot set status after HTTP headers have been sent " 如果我删除 response.End(),我会在 ajax 错误处理程序方法中得到 401 响应,而不是 403。 我试过了

 HttpContext.Current.ApplicationInstance.CompleteRequest()

而不是 response.end,但我也收到 401 错误

有没有办法避免这个异常?

更新 我在 javascript 中使用这个通用错误处理程序方法来捕获所有 ajax 请求错误(我需要正确的状态码来响应):

 function ajaxFailed(xhr, textstatus, errorthrown) 
    debugger;
    if (textstatus == "error") 
        if (xhr.status == 401) 
            window.location = "/Account/Login";
        
        if (xhr.status == 403) 
            $("#divoutput").html("you don't have permission.");
        
    
    if (xhr.status == 500) 
        $("#divoutput").append("<br/> internal servererror." + xhr.responseText);
    

更新 2: 我目前的解决方案: 在我的自定义异常处理程序过滤器中检查引发异常的 Exception.Message 属性,如果消息是:“服务器无法在发送 HTTP 标头后设置状态”,则忽略它。

我知道这是一种低效的方法,但我还没有其他解决方案。等待一个好的解决方案:)

【问题讨论】:

【参考方案1】:

尝试改变处理请求的顺序:

public override void OnAuthorization(AuthorizationContext filterContext)

    CheckIfUserIsAuthenticated(filterContext);
    base.OnAuthorization(filterContext);

更新

你链接的文章说你必须处理其他事件处理方法 HandleUnauthorizedRequest:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)

不像你那样OnAuthorization

更新 #2: 尝试使用缓冲输出

Response.BufferOutput = true;

因此您的服务器被阻止将标头发送回客户端。

【讨论】:

if (filterContext.Result == null) 如果我这样做总是正确的。 那是因为没有执行验证 - 但没有发送标头。 我不明白你。我需要先执行 base.OnAuthorization(filterContext) 以便正确设置 user.Identity.IsAuthenticated 属性。然后在我的自定义方法中,我正在检查这个值以查找它是身份验证问题还是授权问题。 此方法将headers 发送回客户端。之后,您将无法更改响应的状态。 @Sharun 更新了答案。

以上是关于更改 ajax 请求的 response.statuscode 会引发错误“发送 HTTP 标头后服务器无法设置状态”的主要内容,如果未能解决你的问题,请参考以下文章

如果中间发生页面更改,AJAX 请求/响应的数据会发生啥情况

使用ajax请求,模态框调用并更改密码

如何在下拉更改事件中调用 AJAX 请求?

更改光标并防止单击AJAX请求

在 Sencha Touch 2 中更改选项卡时发出 Ajax 请求

ExtJs 6 - 更改ajax更新请求