Hello all,
As you may or may not be aware I have been building a new Umbraco starter kit called CWS Start. In this package I wanted to have a 404 page setup for the site. Currently the way to setup a 404 page is to put a node ID in the umbracosettings.config file. My only concern with this approach is that if you delete the node and recreate the node then the 404 will stop working.
So I decided to ask the Umbraco community how they do 404’s and I got a fantastic response from the community.
http://our.umbraco.org/forum/developers/extending-umbraco/43866-Alternatives-to-404-in-umbracosettingsconfig
Stefan & Lee K gave me some fantastic answers and filled me on the new Request pipeline in Umbraco and a way on how to add a ContentFinder to Umbraco, aka the new way of doing 404 handlers in Umbraco 6.1 and any other type of content finders.
Show me the code!
public class _404iLastChanceFinder : IContentFinder { public bool TryFindContent(PublishedContentRequest contentRequest) { //Check request is a 404 if (contentRequest.Is404) { //Get the home node var home = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetAtRoot().Single(x => x.DocumentTypeAlias == "CWS-Home"); //Get the 404 node var notFoundNode = home.Children.Single(x => x.DocumentTypeAlias == "CWS-404"); //Set Response Status to be HTTP 404 contentRequest.SetResponseStatus(404, "404 Page Not Found"); //Set the node to be the not found node contentRequest.PublishedContent = notFoundNode; } //Not sure about this line - copied from Lee K's GIST //https://gist.github.com/leekelleher/5966488 return contentRequest.PublishedContent != null; } }
And then we need to register this in Umbraco on App Startup like so:
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { //On application starting event... //Add to the ContentFinder resolver collection our custom 404 Content Finder resolver ContentLastChanceFinderResolver.Current.SetFinder(new _404iLastChanceFinder()); }
There are other use cases for IContentFinders and Lee K has a great little example he posted to Gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Umbraco.Core; | |
using Umbraco.Core.Services; | |
using Umbraco.Web.Mvc; | |
using Umbraco.Web.Routing; | |
namespace Our.Umbraco | |
{ | |
public class MyApplication : ApplicationEventHandler | |
{ | |
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) | |
{ | |
ContentFinderResolver.Current.InsertTypeBefore<ContentFinderByNotFoundHandlers, ContentFinderForWhatever>(); | |
base.ApplicationStarting(umbracoApplication, applicationContext); | |
} | |
} | |
// the example here is to have a 'virtual url'. | |
// this is required on a specific DocType, after @level=3 | |
public class ContentFinderForWhatever : IContentFinder | |
{ | |
public bool TryFindContent(PublishedContentRequest contentRequest) | |
{ | |
if (contentRequest != null) | |
{ | |
var path = contentRequest.Uri.GetAbsolutePathDecoded(); | |
var parts = path.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries); | |
if (parts.Length > 2) | |
{ | |
var level3 = string.Concat('/', string.Join("/", parts.Take(3)), '/'); | |
var node = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute(level3); | |
if (node.DocumentTypeAlias == "Whatever") | |
contentRequest.PublishedContent = node; | |
} | |
} | |
return contentRequest.PublishedContent != null; | |
} | |
} | |
} |
hey warren
Nice example, tryed it but it dosnt redirect to my page it still shows the old “ugly error” page ?
did u set any settings to make it by pass that ?
LikeLike
No it just worked for me. Recommend attaching to process and putting a break point in to see if it fires. Remember this for newer Umbraco 6.1+ sites
LikeLike
hey warren
hmmm i upgraded a site from 6.0.2 to 6.1.4 to get this to work and the code is firering as u say i can see it hits my breakpoint. but it shows me the old one any ideas on what configuration i am missing ?
LikeLike
Hiya that sounds very odd.
You have to pass a content node back for it to display.
Can you post your code on a Gist please. But my suspicion is that the node you are trying to find in the site as your 404 node can’t be found and hence returning null.
LikeLike
ok got it working on a clean 6.1.2 install but my upgraded one is still not working.
btw I don’t think u need to set the status code once more cause u already have a 404 request, due to your if(contentRequest.Is404)
cheers for and awsome blog mate.
LikeLike
Thanks for this… I found it only works if you use
ContentLastChanceFinderResolver.Current.SetFinder(new _404iLastChanceFinder());
LikeLike
Hi Warren, this certainly seems like I better way to do things!
I found that I had to make a few changes to get your solution to work however. Namely contentRequest.Is404 was always set to false when I tried it and so the code inside the check wasn’t run. Updated in a gist here: https://gist.github.com/tcmorris/4f23489db83e87e32706
LikeLike
Very nice implementation of 404 handling.
Doing coding and testing I ran into a problem with getting the correct PublishedContent node to show. This could be due to two reasons. Reason 2 was my fix:
1. Check umbracoSettings.config and make sure your not handling error pages here in the section. If so, comment them out while testing. ( tag must be present).
2. This will not work if web.config and is set. Instead change the ‘existingResponse’ setting to “Passthrough”. However I ended up not using at all and went back to to handle a generic statusCode 500 html error page.
LikeLike
Hey Warren, four years on, is this still the best/only way to implement 404 handling?
Also — or mainly — as a noob I’m stuck. I don’t see an App-Start folder in the project I’m working on. Where do I put that override method? Which class?
Thanks.
LikeLike
Hi Warren,
It’s seems like this doesn’t work for pages of which the document type has no template. It still shows the default (“ugly”) 404 error page. Do you perhaps know how to solve this?
LikeLike