服务器不能在HTTP头已发送后设置状态,IIS7.5。

76
有时我在生产环境中会遇到异常:
 
     
  • 进程信息      
       
    • 进程 ID:3832
    •  
    • 进程名称:w3wp.exe
    •  
    • 帐户名称:NT AUTHORITY\NETWORK SERVICE
    •  
  •  
  • 异常信息     
       
    • 异常类型:System.Web.HttpException
    •  
    • 异常信息:服务器无法在 HTTP 标头已发送后设置状态。
    •  
  •  
  • 请求信息     
       
    • 请求 URL:http://www.myulr.pl/logon
    •  
    • 请求路径:/logon
    •  
    • 用户主机地址:10.11.9.1
    •  
    • 用户:user001
    •  
    • 已验证:True
    •  
    • 身份验证类型:Forms
    •  
    • 线程帐户名称:NT AUTHORITY\NETWORK SERVICE
    •  
  •  
  • 线程信息     
       
    • 线程 ID:10
    •  
    • 线程帐户名称:NT AUTHORITY\NETWORK SERVICE
    •  
    • 正在模拟:False
    •  
  •  
Stack trace: at System.Web.HttpResponse.set_StatusCode(Int32 value) at  
System.Web.HttpResponseWrapper.set_StatusCode(Int32 value) at  
System.Web.Mvc.HandleErrorAttribute.OnException(ExceptionContext filterContext) at  
System.Web.Mvc.ControllerActionInvoker.InvokeExceptionFilters(ControllerContext controllerContext, IList(1) filters, Exception exception) at  
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) at System.Web.Mvc.Controller.ExecuteCore() at  
System.Web.Mvc.MvcHandler.<>c__DisplayClass8.<BeginProcessRequest>b__4() at  
System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0() at  
System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8(1).<BeginSynchronous>b__7(IAsyncResult _) at  
System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult(1).End() at   
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) at  
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at  
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& ompletedSynchronously) 

我在测试环境中没有注意到这个错误,我应该检查什么?

我正在使用 ASP.NET MVC 2 (Release Candidate 2)。


我也遇到了这个问题。错误来自于HandleErrorAttribute内部,就像你的情况一样。 - Gabe Moothart
1
你能解决这个问题吗? - Azho KG
3
自从转换到MVC 2以来,我一直遇到同样的问题。在设置cookie时也会出现类似的错误——在头部已发送后不能再设置。我对答案很好奇。在我的情况下,切换http / https似乎起到了一定作用。 - Matt Sherman
3
我发现我尝试在Actionfilter(OnActionExecuting)中重定向用户——这是一个问题。 - marcinn
10个回答

64

我基本同意Vagrant的看法:

  1. 你的操作正在执行,将标记写入响应流中
  2. 流是未缓冲的,强制在标记写入之前写入响应头。
  3. 你的视图遇到了运行时错误
  4. 异常处理程序启动,试图将状态代码设置为其他非200的值
  5. 失败,因为标头已经被发送。

我与Vagrant意见不同的地方在于“解决绑定错误的方法不会导致错误”-您仍然可能会在视图绑定中遇到运行时错误,例如空引用异常。

更好的解决方案是确保在向响应流发送任何字节之前,Response.BufferOutput = true;,例如在控制器操作或应用程序的On_Begin_Request中。这使得可以设置服务器传输、cookie/标头等,一直到自然结束响应或调用end/flush。

当然,还要检查栈中是否有进一步的刷新/设置缓冲区为false。

MSDN参考:HttpResponse.BufferOutput


1
我遇到了相同的错误,但在我的情况下,当我尝试调用一个子操作时它会发生。我尝试在父操作开头设置Response.BufferOutput = true,但这并没有帮助。有什么想法吗? - Samo
Samo,在你的代码中的某个地方,有些东西正在向响应流写入字节,请在请求处理的开始处设置缓冲区mu,例如在控制器中让你的Action开始之前。 - stephbu
如何在MVC控制器中使用HttpResponse.BufferOutput - Kiquenet
在我的索引动作控制器中,我有:[HttpPost][ValidateAntiForgeryToken] - Kiquenet
(假设您正在谈论System.Web.Mvc.Controller)Controller携带一个Response访问器,以便访问当前HTTP请求上下文的HttpResponse对象。因此,在控制器方法中,以下内容是有效的:this.Response.BufferOutput - stephbu
默认情况下,Response.BufferOutput为true。 - FLICKER

52

仅仅是为了补充之前的回答。当我第一次使用ASP.Net MVC并在控制器动作期间执行Response.Redirect时,我也遇到了相同的问题:

Response.Redirect("/blah", true);

我本应该返回一个 RedirectAction,而不是返回一个 Response.Redirect 操作:

return Redirect("/blah");

1
谢谢,我在我的过滤器中也是这样做的。更改后问题得到解决... //Url助手生成重定向URL。 UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext); filterContext.Result = new RedirectResult(urlHelper.Action(ActionNames.Index, ControllerNames.CustomerSearch, new{Area = ""}));//最后 base.OnActionExecuting(filterContext); - learning...
2
我曾经遇到过与.NET CAS客户端类似的问题,这个答案让我意识到了我的问题所在。我本质上是返回了两次响应,先调用了CasAuthentication.SingleSignOut();然后又调用了RedirectToAction()。显然这是没有意义的——你怎么可能重定向两次呢?但当时我没有意识到这一点。我将我的LogOut操作的返回类型从ActionResult更改为void,摆脱了重定向,问题得到了解决。谢谢! - Pandincus

16
HTTP 服务器在您指定错误或开始发送数据之前不会将响应标头返回给客户端。如果您开始向客户端发送数据,则服务器必须首先发送包含状态码的响应头。一旦头部已发送,您就不能再在头部中放置状态代码了。
通常的问题是这样的。您启动页面并发送一些初始标记(即 <head>)。然后服务器会发送这些标记到客户端,但会先发送带有默认成功状态的 HTTP 响应头。现在您开始编写页面内容并发现了一个问题。此时您无法发送错误,因为响应头已经被发送了,而响应头中应该包含错误状态。
解决方案如下:在生成任何内容之前,请确保不会出现任何错误。只有在您确认没有问题后才能开始发送内容,例如 标签
在您的情况下,似乎您有一个处理来自表单的 POST 请求的登录页面。您可能会丢弃一些初始 HTML,然后检查用户名和密码是否有效。相反,在生成任何 HTML 之前,您应该先验证用户/密码。

13

我在设置AuthorizeAttribute方法的HandleUnauthorizedRequest中,尝试设置StatusCode后使用Response.End时遇到了同样的问题。

var ctx = filterContext.HttpContext;
ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;
ctx.Response.End();

如果您正在使用.NET 4.5+,请在Response.StatusCode之前添加此行代码。

filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;

如果您正在使用.NET 4.0,请尝试SuppressFormsAuthenticationRedirectModule


1
还要注意,仅使用 Response.End(); 也有问题,因为它会隐式地发送 HTTP 200 状态码。一般来说,我们应该避免手动操作 Response 对象,而是抛出/返回适当的异常/响应对象,让 ASP.NET 自己处理。 - Zero3

11

在执行重定向之前,可以先检查一下这个:

if (!Response.IsRequestBeingRedirected)
{
   //do the redirect
}

3

您实际上正在尝试重定向页面,但该页面具有某些响应以抛出。因此,首先在页面开头使用response.buffer = true将您拥有的信息保存到缓冲区中,然后在需要时使用response.flush进行刷新,这个错误就会被修复。


1
我记得这个异常的一部分是在PHP中发生的:“无法修改头信息 - 头已经被发送”。当重定向阶段已经发送了标头并生成了任何其他输出时,它就会发生,例如:

echo "hello"; header("Location:http://stackoverflow.com");

请原谅我并纠正我,如果我错了,因为我还在学习MS技术,我正在尝试帮助。


1

我们遇到了相同的错误 - 所以这可能对一些人有用。

对于我们来说,原因非常简单。接口更改让最终用户感到困惑,他们在表单提交后在“不好”的时间按下浏览器中的返回按钮(当然,我们应该使用PRG模式,但我们没有这样做)。

我们解决了这个问题,用户不再按返回按钮了。问题解决了。


1

很抱歉,我在这个帖子中加入了我的两分钱,以防万一有人遇到同样的问题。

  • 我在我的MVC应用程序中使用了表单验证。
  • 但是某些控制器操作是“匿名”的,即允许非身份验证用户。
  • 有时在这些操作中,我仍然希望在某些条件下将用户重定向到登录表单
  • 为了做到这一点-我在我的动作方法中有这个:return new HttpStatusCodeResult(401)-ASP.NET非常好地检测到这一点,并将用户重定向到登录页面!神奇,对吧?它甚至有正确的ReturnUrl参数等。

但你看到我在这里得到了什么吗?我返回401。而ASP.NET会重定向用户。这本质上就是返回302。一个状态码被另一个替换。

而一些IIS服务器(只有一些!)会抛出此异常。有些则不会。-我没有在我的测试服务器上遇到它,只有在我的生产服务器上(难道不总是这种情况吗?o_O)

我知道我的答案基本上重复了这里已经说过的内容,但有时很难弄清楚这种覆盖发生的确切位置。


0

如果有人仍然遇到这个问题,请尝试使用“代替重写”

 public void OnActionExecuting(ActionExecutingContext context)
    {
        try
        {

            if (!HttpContext.Current.User.Identity.IsAuthenticated)
            {
                if (!HttpContext.Current.Response.IsRequestBeingRedirected)
                {

                    context.Result = new RedirectToRouteResult(
                new RouteValueDictionary {  { "controller", "Login" }, { "action", "Index" } });
                }
            }

        }
        catch (Exception ex)
        {
               new RouteValueDictionary { { "controller", "Login" }, { "action", "Index" } });
        }

    }

在控制器中的OnActionExecuting?过滤器? - Kiquenet
@Kiquenet 在过滤器中 - Emre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接