Ben Mills
Ben Mills

Reputation: 28624

Best way to implement a 404 in ASP.NET

I'm trying to determine the best way to implement a 404 page in a standard ASP.NET web application. I currently catch 404 errors in the Application_Error event in the Global.asax file and redirect to a friendly 404.aspx page. The problem is that the request sees a 302 redirect followed by a 404 page missing. Is there a way to bypass the redirect and respond with an immediate 404 containing the friendly error message?

Does a web crawler such as Googlebot care if the request for a non existing page returns a 302 followed by a 404?

Upvotes: 37

Views: 66931

Answers (9)

Zhaph - Ben Duguid
Zhaph - Ben Duguid

Reputation: 26976

Handle this in your Global.asax's OnError event:

protected void Application_Error(object sender, EventArgs e){
  // An error has occured on a .Net page.
  var serverError = Server.GetLastError() as HttpException;

  if (serverError != null){
    if (serverError.GetHttpCode() == 404){
      Server.ClearError();
      Server.Transfer("/Errors/404.aspx");
    }
  }
}

In you error page, you should ensure that you're setting the status code correctly:

// If you're running under IIS 7 in Integrated mode set use this line to override
// IIS errors:
Response.TrySkipIisCustomErrors = true;

// Set status code and message; you could also use the HttpStatusCode enum:
// System.Net.HttpStatusCode.NotFound
Response.StatusCode = 404;
Response.StatusDescription = "Page not found";

You can also handle the various other error codes in here quite nicely.

Google will generally follow the 302, and then honour the 404 status code - so you need to make sure that you return that on your error page.

Upvotes: 49

ubik404
ubik404

Reputation: 621

I really like this approach: it creates a single view to handle all error types and overrides IIS.

[1]: Remove all 'customErrors' & 'httpErrors' from Web.config

[2]: Check 'App_Start/FilterConfig.cs' looks like this:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

[3]: in 'Global.asax' add this method:

public void Application_Error(Object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Server.ClearError();

    var routeData = new RouteData();
    routeData.Values.Add("controller", "ErrorPage");
    routeData.Values.Add("action", "Error");
    routeData.Values.Add("exception", exception);

    if (exception.GetType() == typeof(HttpException))
    {
        routeData.Values.Add("statusCode", ((HttpException)exception).GetHttpCode());
    }
    else
    {
        routeData.Values.Add("statusCode", 500);
    }

    Response.TrySkipIisCustomErrors = true;
    IController controller = new ErrorPageController();
    controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    Response.End();
}

[4]: Add 'Controllers/ErrorPageController.cs'

public class ErrorPageController : Controller
{
    public ActionResult Error(int statusCode, Exception exception)
    {
        Response.StatusCode = statusCode;
        ViewBag.StatusCode = statusCode + " Error";
        return View();
    }
}

[5]: in 'Views/Shared/Error.cshtml'

@model System.Web.Mvc.HandleErrorInfo
@{
    ViewBag.Title = (!String.IsNullOrEmpty(ViewBag.StatusCode)) ? ViewBag.StatusCode : "500 Error";
}

 <h1 class="error">@(!String.IsNullOrEmpty(ViewBag.StatusCode) ? ViewBag.StatusCode : "500 Error"):</h1>


//@Model.ActionName
//@Model.ContollerName
//@Model.Exception.Message
//@Model.Exception.StackTrace

:D

Upvotes: 3

Eyal
Eyal

Reputation: 4773

I also faced with 302 instead 404. I managed to fix it by doing the following:

Controller:

public ViewResult Display404NotFoundPage()
        {
            Response.StatusCode = 404;  // this line fixed it.

            return View();
        }

View:

Show some error message to user.

web.config:

<customErrors mode="On"  redirectMode="ResponseRedirect">
      <error statusCode="404" redirect="~/404NotFound/" />
</customErrors>

Lastly, the RouthConfig:

routes.MapRoute(
             name: "ErrorPage",
             url: "404NotFound/",
             defaults: new { controller = "Pages", action = "Display404NotFoundPage" }
         );

Upvotes: 3

James
James

Reputation: 12806

I think the best way is to use the custom errors construct in your web.config like below, this let's you wire up pages to handle all of the different HTTP codes in a simple effective manner.

  <customErrors mode="On" defaultRedirect="~/500.aspx">
     <error statusCode="404" redirect="~/404.aspx" />
     <error statusCode="500" redirect="~/500.aspx" />
  </customErrors>

Upvotes: 0

Ben Mills
Ben Mills

Reputation: 28624

I can see that setting up the 404 page in the web.config is a nice clean method, BUT it still initially responds with a 302 redirect to the error page. As an example, if you navigate to:

https://stackoverflow.com/x.aspx

you'll be redirected via a 302 redirect to:

https://stackoverflow.com/404?aspxerrorpath=/x.aspx

What I want to happen is this:

http://www.cnn.com/x.aspx

There's no redirect. A request for the missing URL returns a 404 status code with a friendly error message.

Upvotes: 2

Ian G
Ian G

Reputation: 30224

Do you use this anywhere?

 Response.Status="404 Page Not Found"

Upvotes: 2

Pontus Gagge
Pontus Gagge

Reputation: 17278

Easiest answer: don't do it in code, but configure IIS instead.

Upvotes: 3

Robin Day
Robin Day

Reputation: 102578

You can use the web.config to send 404 errors to a custom page.

    <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
        <error statusCode="403" redirect="NoAccess.htm" />
        <error statusCode="404" redirect="FileNotFound.htm" />
    </customErrors>

Upvotes: 15

Assaf Lavie
Assaf Lavie

Reputation: 76153

You can configure IIS itself to return specific pages in response to any type of http error (404 included).

Upvotes: 0

Related Questions