【问题标题】:ASP.NET MVC 3: Server cannot append header after HTTP headers have been sentASP.NET MVC 3:发送 HTTP 标头后服务器无法附加标头
【发布时间】:2012-01-20 11:43:13
【问题描述】:

我们正忙着将使用 3.5 框架的 ASP.NET MVC 2 应用程序升级到在 4.0 框架上运行的 ASP.NET MVC 3 应用程序。

有一个页面在使用浏览器后退按钮访问时会引发异常。为了支持此页面上的浏览器后退按钮,我们实现了一个系统,该系统在返回页面时重新请求该页面的结果。我没有明确的指示在哪里寻找问题,但是因为我总是只找到错误

Server cannot append header after HTTP headers have been sent.

使用堆栈跟踪

at System.Web.HttpResponse.AppendHeader(String name, String value)
at System.Web.HttpResponseWrapper.AppendHeader(String name, String value)
at System.Web.Mvc.MvcHandler.AddVersionHeader(HttpContextBase httpContext)
at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<BeginProcessRequest>b__2()
at System.Web.Mvc.SecurityUtil.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a()
at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust[TResult](Func`1 func)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

为什么已经发送了 HTTP 标头?

提前谢谢你, 伊万L

编辑: 我正在添加在寻找这个问题时获得的新信息和见解。提到其中一个答案的异步控制器让我想知道。当我发现我必须更改以下内容才能使旧的 MVC2 方法起作用时:

[HttpPost, ValidateInput(false)]
public void SearchResultOverview(SearchResultViewModel model, string searchUrl)
{
    if (!string.IsNullOrEmpty(searchUrl))
    {
        searchUrl = searchUrl.Replace("SearchPartial", "SearchPartialInternal");

        //NOTE MVC 3
        HttpContext.Server.TransferRequest(searchUrl, true);

        //NOTE MVC 2
        //System.Web.HttpContext.Current.RewritePath(searchUrl, false);

        //IHttpHandler httpHandler = new MvcHttpHandler();
        //// Process request
        //httpHandler.ProcessRequest(System.Web.HttpContext.Current);
    }
}

当我查看 TransferRequest 方法时,我发现它是Performs an asynchronous execution of the specified URL and preserves query string parameters. (http://msdn.microsoft.com/en-us/library/system.web.httpserverutility.transferrequest.aspx)

在我发布异常之前还有一个异常被抛出(我只是错过了它,因为我很晚才咳出异常)。这个例外是:

The SessionStateTempDataProvider class requires session state to be enabled.
   at System.Web.Mvc.SessionStateTempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary`2 values)
   at System.Web.Mvc.TempDataDictionary.Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
   at System.Web.Mvc.Controller.PossiblySaveTempData()
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
   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.<>c__DisplayClasse.<EndProcessRequest>b__d()
   at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
   at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

那么我该如何做呢?

【问题讨论】:

  • 好吧,让我看看我能不能从心理上推断出这个…………嗯……不……你必须提供一些代码。您要附加哪些标头,何时附加?
  • 也许这会有所帮助:stackoverflow.com/questions/2383169/…
  • @Mystere Man:这正是重点。我自己没有附加任何标题,似乎从 MVC2 更改为 MVC3 会产生这个问题,因为代码运行良好,在 MVC2 中没有任何异常。如果您查看堆栈跟踪,您会注意到:at System.Web.Mvc.MvcHandler.AddVersionHeader(HttpContextBase httpContext) 这意味着 MVC 本身正在尝试添加标头,但它不能。我确实尝试了您将我与reponse.BufferOutput = true 联系起来的问题的解决方案,但它不起作用。我总是收到这个异常和堆栈跟踪。
  • 为什么要在 action 方法中创建一个新的 HttpHandler?
  • @Andrew Barber:这样做是为了处理大型 URL。字符串 searchUrl 是实际的 URL,但它太大以至于一些浏览器拒绝使用它。为了规避这一点,URL 实际上已发布并在内部处理。在 MVC 2 中,这是通过启动一个新的请求处理程序并让它处理实际 URL 来完成的,因为 MVC 3 这不再可能,我为此找到的解决方案是使用Server.TransferRequest()。然而,当浏览器后退按钮接近时,这种方法似乎不起作用......(当页面定期加载时它完美地起作用)

标签: asp.net asp.net-mvc-3 asp.net-mvc-2 iis-express


【解决方案1】:

好消息,今天我在对异常原因进行了更深入的研究后解决了自己的问题。帮助我了解异常和错误的确切原因的第一个链接如下:http://www.eggheadcafe.com/tutorials/asp-net/79c73563-408a-493e-a369-d4b380bce549/aspnet-using-servertransferrequest.aspx

它详细介绍了 Server.TransferRequest 的工作原理,并提到了所有重要的警告:会话必须在传输到子请求之前由主请求释放。深入研究我将如何使用 MVC 执行此操作时,我在 stackoverflow 上看到了以下帖子:How to simulate Server.Transfer in ASP.NET MVC?

这篇文章反过来又向我指出了一个非常重要的事情:throw new ApplicationException("TempData won't work with Server.TransferRequest!"); 所以我创建了可以在该帖子中找到的 TransferResult 类,并让需要它的操作通过该点返回。我现在发现这个异常在我之前提到的特定情况下受到了影响。我自己从未使用过 TempData,但显然我的一位同事使用过。

由于内部不重要数据的性质,我决定在任何Server.TransferRequest() 之前使用Clear() TempData,这使我的异常和问题像雪一样融化在阳光下。

我要感谢所有希望解决这个问题的人,并且很高兴我能提供一个结束的结论和解决方案,以便那些正在研究同样问题的人受益。

此致,伊万L

【讨论】:

  • 你知道为什么我们需要在调用 TransferRequest 之前检查 TempData 中的数据吗?
  • @yurart 如上所述,必须释放会话状态,并且 tempdata 似乎“锁定”了会话,使您无法传输请求。
【解决方案2】:

如果页面上的缓冲已关闭,则可能会发生这种情况。缓冲意味着 asp.net 在发送响应之前等待整个请求完成。这意味着可以随时更改标题。当缓冲关闭时,输出会在生成时发送到客户端。因此,您不能随意更改标头,因为它们已经发送了。

从您的堆栈跟踪来看,它似乎是一个异步控制器,我想知道这是否与它有关。不过,我只是根据您发布的内容进行猜测。

更新

更正,异步提及实际上是框架代码,与您的代码无关。但是,从您上面的代码来看,SearchResultOverview 是对控制器的操作吗?如果是这样,那么使用您用来转移执行的方法是我认为您的问题的原因。

它导致 2 个 mvchandler 执行并且它们相互干扰。路由将是重定向请求的更好方法。

【讨论】:

  • 我会参考我在回复神秘人时写的评论。我试图在 BeginRequest 中启用缓冲,但它没有改变任何东西。 MVC2 和 MVC3 之间有什么变化,突然头被提前发送,我需要开始改变缓冲之类的行为?你说它似乎是一个异步控制器,但据我所知,MVC2 中不存在这个概念,这纯粹是一个使用 ASP 编写的 codeplex 的更新工具从 MVC2 升级到 MVC3 的项目。 NET 团队。
  • 确实,我对异步的看法是错误的。 MVC 3 通过它处理一切。这是一个框架,而不是你的代码。我
【解决方案3】:

我记得前段时间遇到过这个问题。我不记得确切的条件,但它与直接在自定义操作过滤器中设置标头和 http 状态代码有关。

我当时的目标是在用户身份验证超时并单击 ajax 操作链接时显示自定义页面/消息(例如,当他将页面打开一段时间然后他回来并单击链接时),所以 asp.net mvc 没有在 div 中显示默认登录页面(有点难看)。我现在手头没有代码,但它是这样的:

public class AjaxFilterAttribute : ActionFilterAttribute  
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.HttpContext.Response.AddHeader("name", "value");
            filterContext.HttpContext.Response.StatusCode = 200;
            filterContext.Result = something;
        }

        base.OnActionExecuting(filterContext);
    }
}

问题是,在早期版本的 asp.net mvc 上尝试相同的代码给了我“无法附加标头”错误。我不记得我是如何修复它的,但无论如何这并不容易。如果您认为这种情况适用于您,我可以在我的旧项目中搜索固定代码。

希望对你有帮助

【讨论】:

  • 想知道为什么投反对票?也许这不适用于提问者遇到的确切问题,但它可以帮助其他人。哎呀...
  • 我也面临同样的问题。唯一的区别是我在 OnActionExecuted 中有逻辑。你介意分享修复吗?
  • 我没有,及时丢失了……但关键是直接设置结果,比如:filterContext.Result = new HttpUnauthorizedResult();跨度>
  • 反对票可能是因为您的答案不是答案。 “我遇到了这个问题,但我不记得如何解决它”。笏。
猜你喜欢
  • 2011-09-13
  • 2011-02-21
  • 2023-03-08
  • 2015-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 2014-06-10
相关资源
最近更新 更多