7

I had to implement a custom HttpModule to handle a 404 error in Sharepoint.

It listens for the PreSendRequestContent event, and looks for a 404 status code. If one is found it does a TransferRequest.

void App_PreSendRequestContent(object sender, EventArgs e)
{
    HttpResponse res = App.Response;
    HttpRequest req = App.Request;

    if (res.StatusCode == 404 && !req.Url.AbsolutePath.Equals(PageNotFoundUrl, StringComparison.InvariantCultureIgnoreCase))
    {
        App.Server.TransferRequest(PageNotFoundUrl);
    }
}

This works just fine, but I noticed in Fiddler that the page is showing a 200 status code, even though the original request was a 404. This is not good for search engines.

Is this an expected behaviour of TransferRequest? Can I somehow maintain the 404 status code? Or, would I have been better off using a good old fashioned Server.Transfer?

Update

I tried this outside of a sharepoint environment, and the Server.TransferRequest request does indeed give a 200 status code, removing the 404. Server.Transfer doesn't work as I don't think it can given the pipeline.

Update 2

Thanks to the answer below, I have added the following:

void App_PostRequestHandlerExecute(object sender, EventArgs e)
{
    HttpResponse res = App.Response;
    HttpRequest req = App.Request;

    if (req.Url.AbsolutePath.Equals(PageNotFoundUrl, StringComparison.InvariantCultureIgnoreCase))
    {
        res.StatusCode = 404;
    }
}
ScottE
  • 21,530
  • 18
  • 94
  • 131

2 Answers2

2

Server.TransferRequest doesn't change the status code. It passes control of the request on to another page without telling the client. You can manually set the status code in your custom 404 page in the Page_Load event:

protected void Page_Load(object sender, System.EventArgs e)
{
    Response.StatusCode         = 404;
    Response.StatusDescription  = "Not Found";
}
David
  • 34,223
  • 3
  • 62
  • 80
  • This makes sense. Unfortunately in sharepoint this means deploying a web part that issues this status code...but it works just fine otherwise. – ScottE Feb 14 '11 at 21:41
  • Also, I don't think you're correct in your first paragraph - try it yourself. Set a 404 status code on a page, then do a Server.TransferRequest() - you'll see the 404 turn into whatever the status code that is returned by the page that the request is transferred to. BTW, when I say 'see', I don't mean that both headers will be present, just that testing on/off will demonstrate this. – ScottE Feb 14 '11 at 21:44
  • It works if you set the status code in the destination page of the Server.TransferRequest. This is what I do for my site. – David Feb 15 '11 at 04:37
2

Well, TransferRequest() triggers a new request, which implies a new response. Since the resource that PageNotFoundUrl points to does exist, the client receives a legitimate 200 OK status header.

You might want to write an HTTP handler (or handle an event in Global.asax) in order to force the status header to 404 Not Found when serving PageNotFoundUrl.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • This also makes sense, especially since I already have a httpmodule in place. Where in the pipeline should I check for this? – ScottE Feb 14 '11 at 21:49
  • @ScottE, [PostRequestHandlerExecute](http://msdn.microsoft.com/en-us/library/system.web.httpapplication.postrequesthandlerexecute.aspx) should be appropriate. – Frédéric Hamidi Feb 14 '11 at 21:52
  • Perfect, I like it, thanks. Question updated to include this solution. – ScottE Feb 14 '11 at 21:58
  • In IIS7, I can't change the status code. If I do, IIS will just take over the response and render its own HTTP body regardless of what I might have rendered in my custom error handling. – Asbjørn Ulsberg Apr 29 '11 at 14:40
  • 1
    @asbjornu: Response.TrySkipIisCustomErrors = true; – bang Aug 28 '14 at 18:34