5

We're busy upgrading an ASP.NET MVC 2 Application using the 3.5 framework to an ASP.NET MVC 3 Application running on the 4.0 framework.

There's a page which throws an exception when approached by using the browser back button. To support the browser back button on this page we implemented a system that requests the results for that page anew when arriving back on the page. I have no clear indications where to look for the problem however since I always only find the error

Server cannot append header after HTTP headers have been sent.

With stacktrace

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)

How come the HTTP headers already have been sent?

Thank you in advance, IvanL

EDIT: I am adding new information and insight I gained while hunting for this problem. The Asynch Controller mention of one of the answers got me wondering. When I found that I had to change the following for the old MVC2 method to work:

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

When I looked up the TransferRequest method I found that it 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 )

Also there is an Exception being thrown before the exception I posted (I simply missed out on it since I cought the exception late). This exception is:

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)

So how do I make this work?

IvanL
  • 2,475
  • 1
  • 26
  • 39
  • 3
    Well, let me see if i can deduce this psychicly... ... ... umm.. nope.. you're going to have to provide some code. What headers are you appending, and when? – Erik Funkenbusch Dec 14 '11 at 17:42
  • 1
    Perhaps this will help: http://stackoverflow.com/questions/2383169/server-cannot-set-status-after-http-headers-have-been-sent-iis7-5 – Erik Funkenbusch Dec 14 '11 at 17:46
  • 1
    @Mystere Man: That's exactly the point. I'm not appending any headers myself, it seems that changing from MVC2 to MVC3 created this problem since the code runs fine without any exceptions in MVC2. If you look in the stacktrace you notice: `at System.Web.Mvc.MvcHandler.AddVersionHeader(HttpContextBase httpContext)` which means MVC itself is trying to add a header, but it can't. I did try the solution of the question you linked me with `reponse.BufferOutput = true` but it does not work. I always receive this exception and stacktrace. – IvanL Dec 15 '11 at 09:33
  • Why are you creating a new HttpHandler inside an action method? – Andrew Barber Dec 15 '11 at 14:25
  • @Andrew Barber: This was done to handle large URLs. The string searchUrl is the actual URL, but it is so large that some browsers refuse working with it. To circumvent this the URL was actually posted and internally handled. In MVC 2 this was done by firing up a new requesthandler and letting it handle the actual URL, since MVC 3 this is no longer possible and the solution I found for this was to use `Server.TransferRequest()`. It appears however that this method does not work when approached by the browser back button... (it perfectly works when the page is regularly loaded) – IvanL Dec 16 '11 at 14:01

3 Answers3

3

Good news, today I solved my own problem after more thorough research on the cause of the exceptions. The first link that helped me understand what exactly might be the cause of my exceptions and errors was the following: http://www.eggheadcafe.com/tutorials/asp-net/79c73563-408a-493e-a369-d4b380bce549/aspnet-using-servertransferrequest.aspx

It details the workings of Server.TransferRequest and mentions the all important caveat: Session must be released by the main request before transferring to the child request. Digging deeper into how I would do this with MVC I came across the following post here on stackoverflow: How to simulate Server.Transfer in ASP.NET MVC?

This post in turn pointed me to an extremely important matter to know: throw new ApplicationException("TempData won't work with Server.TransferRequest!"); So I created the TransferResult class that can be found in that post and let the actions that required it return via that point. I found now that this exception was being hit in the specific cases I mentioned before. I myself never used TempData but apparently one of my colleagues did.

Due to the nature of the unimportant data inside I decided to Clear() the TempData before any Server.TransferRequest() which made my exceptions and problems melt like snow to the sun.

I want to thank all people that looked to solve this problem and am glad I can provide a closing conclusion and solution so it may benefit those that are looking at the same problem.

Sincerely, IvanL

Community
  • 1
  • 1
IvanL
  • 2,475
  • 1
  • 26
  • 39
  • Do you know why we need to check for data in TempData before calling TransferRequest? – yurart Feb 28 '17 at 14:14
  • 1
    @yurart As mentioned above session state must be released and tempdata seems to 'lock' the session making you unable to transfer the request. – IvanL Feb 28 '17 at 15:46
2

This can happen if buffering has been switched off on the page. Buffering means that asp.net waits for the whole request to be completed before sending the response. This means the header can be changed at any time. When buffering is off, the output is sent to the client as it's generated. You can't therefore change the headers at will, as they've already been sent.

From your stacktrace, it appears to be an async controller & I'm wondering if this has anything to do with it. I'm only guessing from what you've posted though.

Update

Correction, the async mention is actually framework code & nothing to do with your code. However from your code above, is SearchResultOverview an action on a controller? If so, then using the methods you are using to transfer execution is I think the cause of your problems.

It causing 2 mvchandlers to execute & they're interfering with each other. Routing would be a better way to redirect the request.

Simon Halsey
  • 5,459
  • 1
  • 21
  • 32
  • I will refer to the comment I wrote in reply to Mystere Man. I tried to enable the buffering in the BeginRequest but it did not change anything. What changes between MVC2 and MVC3 that suddenly headers are being send earlier and I need to start changing behaviours like buffering? You say it appears to be an async controller but that concept didn't exist in MVC2 as far as I'm aware of and this is purely a project that was upgraded from MVC2 to MVC3 using the update tool from codeplex written by the ASP.NET team. – IvanL Dec 15 '11 at 09:49
  • Indeed, I was wrong about the async. MVC 3 handles everything through that. It'd a framework thing, not your code. I – Simon Halsey Dec 16 '11 at 11:40
0

I remember having this problem some time ago. I don't remember the exact conditions, but it had something to do with setting the headers and http status code directly inside a custom action filter.

My goal back then was to show a custom page/message when the user authentication timed out and clicked on an ajax action link (like when he leaves the page open for a while then he comes back and clicks on the link), so asp.net mvc didn't show the default login page inside a div (kind of ugly). I don't have the code at hand right now, but it was something like this:

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

The thing is, trying this same code on an earlier version of asp.net mvc gave me the "cannot append header" error. I don't remember how I fixed it, but it wasn't easy anyway. I can search through my old projects for the fixed code if you think this case applies to you.

Hope it helps

Francisco
  • 4,104
  • 3
  • 24
  • 27
  • Care to tell why the downvote? Maybe this doesn't apply to the exact problem the asker had, but it can help other people. Geez... – Francisco Nov 14 '13 at 17:37
  • I am facing the same issue. The only difference is that I have the logic in OnActionExecuted. Would you mind sharing the fix? – sandesh kota Aug 25 '16 at 13:03
  • I don't have it, it is lost in time... but the key part is to set the result directly, like this for instance: filterContext.Result = new HttpUnauthorizedResult(); – Francisco Aug 29 '16 at 14:16
  • The downvote is probably because your answer is not an answer. "I had this problem, but I don't remember how to fix it". Wat. – jazzcat Oct 26 '16 at 10:57