We love to think that users are proper in how they interact with our applications, or that they are able to do things in a way that is consistent with expectations. But, we all know that isn't actually the way life works! The 404 response is not only important for users but also valuable for bots/search engines to disclose content that has been removed or otherwise unavailable.
As much as we would love to think that this is something that is easy, or something that is set up for us out of the box, it isn't 100% ready to go unless we do a bit more configuration on our part. Let's dive in, starting with the goal, then the solution.
The Goal
The goal here is pretty simple: if a user requests content that isn't found, we want to ensure that we have a response that has the proper HTTP Status code of 404 and that we show a pretty message to the user. We want to do this quickly and with as minimal effort as possible!
The Simple Part - Give a Response
.NET 10 provides a new method on the NavigationManager called NotFound(). This is great, inject the NavigationManager and call this method, and you are done (right?). Well, sort of, this accomplishes our first goal, to give a valid HTTP Response.
However, this response is just a default 404; no UI is returned, just the fault code, end of story.
NOTE: If you used a very recent template for your project and started with .NET 10, you MAY have a slightly better experience.
Making It Pretty
We don't want to overwhelm the visitor with the not-found page, but we do want to ensure that they know a bit about what happened and how they can do things better. To this point we have a few options.
Create a Page & Set NotFound Page
One option is to add a Razor Component to your application, set the @Layout as needed, and render any content that you like for the user. Once the Component is set up, simply add NotFoundPage="typeof(YourPageComponentHere)" to the root "<Router>" node within routes.razor.
An example of a complete routes.razor with this setup is below.
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
<Authorizing>
<SectionLoading />
</Authorizing>
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeRouteView>
</Found>
</Router>
This is great, its explicit, its detailed, but it does make you need to setup a specific page of content within your project. If you just want a quick/short message, you can be a bit simpler in the approach.
Using the NotFound fragment in Router
If you want to keep the content simple, you can self-contain the configuration, so rather than adding a NotFoundPage attribute you can add something similar to the following just after the closing tag of found.
<NotFound>
<LayoutView Layout="typeof(MainLayout)">
<h1>Page not found</h1>
<p>The page you requested could not be found.</p>
</LayoutView>
</NotFound>
This is nice to be self-contained, and help fully understand what happens with routing, but is less practicial if you have complex content that you want to render.
Not found pages and responses are not the "fun" stuff, but they are critical pieces of polish within the applications and often overlooked. Which solution do you use in your projects?