在发送了 HTTP 报头之后,服务器无法设置状态

有时我在生产环境中会遇到异常:

  • 流程信息
    • 进程编号: 3832
    • 进程名: w3wp.exe
    • 帐户名称: NT 权威网络服务
  • 异常信息
    • 异常类型: System.Web.HttpException
    • 异常消息: 发送 HTTP 头后,服务器无法设置状态。
  • 要求信息
    • 请求 URL: http://www.myulr.pl/logon
    • 请求路径:/登录
    • 用户主机地址: 10.11.9.1
    • 用户: user001
    • 已验证: 为真
    • 身份验证类型: 表单
    • 线程帐户名称: NT 授权网络服务
  • 线程信息
    • 线程编号: 10
    • 线程帐户名称: NT 授权网络服务
    • 冒充: 错误
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(发布候选版本2)

157750 次浏览

在指定错误或开始发送数据之前,HTTP 服务器不会将响应头发送回客户端。如果您开始向客户端发送数据,那么服务器必须首先发送响应头(其中包含状态代码)。显然,一旦发送了头文件,就不能再在头文件中放置状态代码。

还是老问题。启动页面,并发送一些初始标记(即 <head>)。然后,服务器将这些标记发送给客户机,在发送具有假定 SUCCESS 状态的 HTTP 响应头之后。现在开始处理页面的主要部分,并发现一个问题。此时无法发送错误,因为包含错误状态的响应标头已经发送。

解决方案是: 在生成任何内容之前,检查是否存在任何错误。只有这样,当您确保不会出现任何问题时,才能开始发送内容,比如标记。

在您的示例中,似乎有一个处理来自表单的 POST 请求的登录页面。您可能会抛出一些初始 HTML,然后检查用户名和密码是否有效。相反,在生成 任何 HTML 之前,应该首先验证用户/密码。

我记得在 PHP 中出现过这样一个例外: “无法修改已经发送的头信息——头信息”。当标头已经在重定向阶段发送并生成任何其他输出时发生,例如:

回应“你好”; 标题(“位置: http://stackoverflow.com”) ;

请原谅我,如果我错了请纠正我,但我仍然在学习微软技术,我试图帮助。

我大体上同意流浪汉的观点:

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

我不同意 Vagrant 的地方是“在绑定中不导致错误”的补救方法——在 View 绑定中仍然可能遇到运行时错误,例如 null 引用异常。

更好的解决方案是确保在将任何字节发送到 Response 流之前使用 Response.BufferOutput = true;。例如,在您的控制器操作或 On _ Begin _ Request 在应用程序中。这使得服务器传输、 cookies/Header 可以设置等等,直到自然结束响应,或者调用 end/rush。

当然,还要检查缓冲区没有被刷新/设置为堆栈中的 false。

MSDN 参考资料: 缓冲输出

只是为了补充上面的回答。当我第一次开始使用 ASP.Net MVC 时,我也遇到了同样的问题,当时我正在做一个响应。在控制器操作期间重定向:

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

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

return Redirect("/blah");

您实际上正在尝试重定向要抛出某个响应的页面。因此,首先,您保持在一个缓冲区中的信息,您已经抛出使用 response.buffer = true在开始的页面,然后刷新它时,需要使用 response.flush这个错误将得到修复

在执行重定向之前检查一下这个怎么样:

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

我有同样的问题,设置 StatusCode,然后 Response.EndAuthorizeAttributeHandleUnauthorizedRequest方法

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

我很抱歉,但我还是要加上我的两分钱以防有人也有同样的问题。

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

但你明白我的意思了吧?我 返回401。然后 ASP.NET 重定向用户。基本上就是 报税表302。一个状态代码被另一个替换。

和一些 IIS 服务器(只是一些!)抛出此异常。有些人不会。- 我在我的测试服务器上没有它,只有在我的生产服务器上(不是总是这种情况吗 o _ O)

我知道我的答案基本上是在重复这里已经说过的话,但有时很难弄清楚这种覆盖究竟是在哪里发生的。

如果有人仍然有这个问题。尝试使用,而不是覆盖

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


}

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

对我们来说,原因非常简单。一个界面的改变使最终用户感到困惑,他们在表单提交后的一个“坏”时间点按下了浏览器中的返回按钮(当然我们可能应该使用 PRG 模式,但是我们没有)。

我们修复了这个问题,用户不再按后退按钮。问题解决了。