A web application built using ASP.NET MVC can break in a few
different places when handling a URL it does not recognise. By default, your
user will receive a rather unfriendly default ASP.NET error page. Ideally, you
should be catching these “not found” errors and displaying a more useful
page.
First let's look at the places you need to watch for broken URLs in an ASP.NET
MVC3 application. Then I'll introduce a simple library you can use to handle
all this.
1. Missing action method on a controller
Imagine we have a controller called HomeController and a default route of
“{controller}/{action}” in the route table. Then a URL with the path
“home/epicfail” will be routed to the HomeController and look for an action
method called “EpicFail”.
If the action method does not exist then the Controller's default
HandleUnknownAction method is called and throws an HttpException, with 404 as
the status code. ASP.NET then shows its nasty error page.
A possible solution is to override HandleUnknownAction and render your friendly
404 page instead of throwing an exception.
2. Missing controller
Given a route of “{controller}/{action}”, the URL “missing/example” will cause
a search for a controller called “MissingController”. If this controller is not
found then a 404 HttpException is thrown.
To catch this, you need to have a custom controller factory
(IControllerFactory) that returns a special “not found” controller to handle
the broken request.
3. Unknown route
If the URL does not match any of the defined routes then a 404 HttpException is
thrown. To intercept this you need to create a “catch-all” route. For example,
“{*any}” and map this to some controller that will then show your friendly 404
page.
4. Deliberate not found response
Sometimes your action methods need to say that a resource is not found. For
example, a row with the given ID was not found in the database anymore. You may
want to reuse a common “Not Found” view.
Introducing NotFoundMvc
Correctly handling all the possible “not found” causes is non-trivial and is
likely to be repeated in many MVC applications. Therefore I created the
NotFoundMvc library.
Add the library to your project via
nuget: Install-Package NotFoundMvc
Or get the source code on GitHub: github.com/andrewdavey/NotFoundMvc
Referencing the NotFoundMvc.dll assembly from your web application project will
cause it to auto-start with the application. (Thank you PreApplicationStartMethod
.)
NotFoundMvc will:
- Handle all “not found” causes and render a friendly error view
- Add a catch-all route to the end of the route table to handle unrecognised
routes
- Wrap the controller factory to handle missing controllers
- Wrap the ActionInvoker of each created controller to handle missing actions
- Provide a NotFoundViewResult you can use to return a NotFound view from your
actions
The wrapper objects pass through to the original implementations. So if, for
example, you're using a custom controller factory already, it will still work
as before.
Installing the nugget package will also add a basic NotFound.cshtml view to the
~\Views\Shared folder. So if you're not using nugget, then please create this
view manually. If you're using aspx views, or something else, change the file
extension as required.
The output of the NotFound view must be at least 512 bytes in length, otherwise
IE and Chrome will show their own error page! The default NotFound.cshtml view includes
padding spaces to avoid this.
The source code for NotFoundMvc contains a sample project to demonstrate basic
usage.