The absolute proper way to do 404 pages in ASP.Net MVC

Websites and webservices should return the proper HTTP status codes as response to requests, along with the possible proper body. When you’re creating a ASP.Net MVC web application it can be a little tricky because of a configuration quirk I quite dislike and didn’t find much info about. They improved this practice immensely in ASP.Net Core web applications. So, I want to make this post to clear it up and get it all in one nice explanation and solution. This post is an improvement to my previous post about 404 pages with classic aspx pages.

ASP.NET Web Applications

Let’s say we have a regular ASP.Net MVC project with a page that lists some items and has a detail page of an item. When users request a detail page of an item, we’ll check the id they provided. We’ll return a ‘404 Not Found’ when no item has the requested id. To make it user friendly, we provide a 404-page fitting in our layout view. We also want to show that 404-page when the visitor attempts to navigate to an URL that is not routed to an action.

This could be the controller containing all actions of the application (Note the change of status code in NotFound):

The web.config would contain this snippet to configure the redirection of the result to that of the NotFound action:

This seems correct, but let’s test this and see what HTTP status codes we receive while we navigate to various nonexistent pages:

That ‘302 Found’ fits wrong there. Some clients might figure out the next page is a 404, but it isn’t supposed to happen to my feeling. The HttpNotFound() result on a nonexistent id got captured by the webserver who executed the given URL and returned its response. The unknown route however is caught by ASP.Net and it just redirects on a custom error by default, hence the 302. So, we change the custom errors to rewrite instead:

The result is even worse:

A ‘500 Internal Server Error’ happened! But why? Because customErrors uses Server.Transfer to pass on the information to the page it rewrites to and Server.Transfer only accepts paths to real existing files. So, it can’t resolve routes and goes into exception. How do we fix this? We give it a path to an actual aspx file:

In that aspx file, we transfer the request to the correct URL with Server.TransferRequest:

When we navigate to the non-existing route now, we immediately get the result we hoped for along with a nice HTML response body to give a user-friendly 404-page:

ASP.NET Core Web Applications

(I improved my solution in the meantime. Please refer to my next post.)

Now .Net Core has come along and made things a whole lot simpler in ASP.Net Core Web Applications. We can use almost the same controller for this example:

To complete things, we just add one piece of middleware to our pipeline in StartUp.cs which will execute the request again to another URL when the response has another status code while maintaining that status code:

And there we go, perfect status page with perfect response. It couldn’t be simpler.
(I heard about this when a video of Channel 9 was shared by MSDN a few days ago. I figured it would be perfect to include and complete the package. Credit goes to them for this bit.)

These are what I currently experience as the absolute proper ways to do custom status code pages in ASP.Net web applications. I hope this guide has helped you well on track to creating a perfect website responding with all the correct HTTP response codes.

Leave a Reply

Your email address will not be published. Required fields are marked *