My goal is to create a multi site 404 handler. I see several tutorials on how to do this. The problem is that I come from a Web Forms background and am not super familiar with MVC. All the examples say to "create this" and then show code. Where do I save this code? What do I name the file? I feel like I am missing a very simple fundamental step.
The short(ish), not entirely technically correct but hopefully understandable, answer:
There are two basic elements to an MVC site - compiled code and non-compiled "code". Non compiled includes, Views and your front end JS etc - you've found you can happily change this stuff.
As a pure front ender you might be finding code examples which are on the compiled side. This means you can't just drop a .cs file (c#) into the website and it expect it to be picked up (something you might be used to with PHP or similar). It will need to be compiled and forms part of a DLL - this code is picked up when the website starts up. Usually you need to compile, build and release this code.
Here's the good news - you can have code that should really be compiled picked up when the website starts (NOTE: use of this technique should be limited as it can get unwieldy quickly and has MANY downsides!!).
Create a folder in the website directory called App_Code - now if you drop a .cs file in there the website will compile it at startup. This means also if you have some error in your syntax you will also break your website so be careful and don't do this development on a live site!
In fact - if you're getting to this point it's time to start using Visual Studio and building releases, trust me the time in learning this stuff will pay off. This route means that you will be able know on build that you've not broken your site and you'll get lots of hints and tips from Intellisense (think of it as Autocomplete / Autocorrect).
But for now..
Create a folder in your website called App_Code
Create a text file titled "CustomFourOhFour.cs"
Paste the below in.. (I've combined the two files you need from the Umbraco documentation into one for ease).
Check the site still starts up!
Create a doctype with the alias fourOhFourPageAlias for each site
using Umbraco.Core;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Core.Composing;
using Umbraco.Web;
using Umbraco.Web.Routing;
using System.Linq;
namespace My.Website
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class SetLastChanceContentFindersComposer : IUserComposer
{
public void Compose(Composition composition)
{
//set the last chance content finder
composition.SetContentLastChanceFinder<My404ContentFinder>();
}
}
public class My404ContentFinder : IContentLastChanceFinder
{
private readonly IDomainService _domainService;
public My404ContentFinder(IDomainService domainService)
{
_domainService = domainService;
}
public bool TryFindContent(PublishedRequest contentRequest)
{
//find the root node with a matching domain to the incoming request
var url = contentRequest.Uri.ToString();
var allDomains = _domainService.GetAll(true);
var domain = allDomains?.Where(f => f.DomainName == contentRequest.Uri.Authority || f.DomainName == "https://" + contentRequest.Uri.Authority).FirstOrDefault();
var siteId = domain != null ? domain.RootContentId : (allDomains.Any() ? allDomains.FirstOrDefault().RootContentId : null);
var siteRoot = contentRequest.UmbracoContext.Content.GetById(false, siteId ?? -1);
if (siteRoot == null) { siteRoot = contentRequest.UmbracoContext.Content.GetAtRoot().FirstOrDefault(); }
if (siteRoot == null)
{
return false;
}
//assuming the 404 page is in the root of the language site with alias fourOhFourPageAlias
IPublishedContent notFoundNode = siteRoot.Children.FirstOrDefault(f => f.ContentType.Alias == "fourOhFourPageAlias");
if (notFoundNode != null)
{
contentRequest.PublishedContent = notFoundNode;
}
// return true or false depending on whether our custom 404 page was found
return contentRequest.PublishedContent != null;
}
}
}
I did a quick bit of hacking around in a v7 version. This will work matching on the domain in the URL - if you're doing the same domain with different cultures you'll need to amend it based on the example in the documentation.
It assumes your home nodes are at the root.
But I like this solution as I hope it's more readable and easy to understand.
Be sure to change the fourOhFour alias to match your doctype (ensure you get the case correct too).
using System;
using System.Linq;
using System.Globalization;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web;
using Umbraco.Web.Routing;
using System.Web;
namespace FourOhFour
{
public class MyApplication : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
// Add our custom 404 content finder - by using a last chance finder it will return 404 response code correctly
ContentLastChanceFinderResolver.Current.SetFinder(new FourOhFourContentFinder());
}
}
public class FourOhFourContentFinder : IContentFinder
{
public bool TryFindContent(PublishedContentRequest contentRequest)
{
var allRoots = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetAtRoot();
var domain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
var rootNode = allRoots.Where(f => f.UrlWithDomain().StartsWith(domain)).FirstOrDefault();
if (rootNode != null)
{
// logic to find your 404 page and set it to contentRequest.PublishedContent
// replace 'fourOhFour' with the alias of your 404 page
IPublishedContent notFoundNode = rootNode.Descendants().Where(x => x.DocumentTypeAlias == "fourOhFour").FirstOrDefault();
if (notFoundNode != null)
{
contentRequest.PublishedContent = notFoundNode;
}
}
return contentRequest.PublishedContent != null;
}
}
}
Thank you so much! I have been looking for this solution for a while. I had to tweak the code a bit to get it to work on my installation, but not much. I marked your post as the answer. Below is the final result.
using System;
using System.Linq;
using System.Globalization;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web;
using Umbraco.Web.Routing;
using System.Web;
namespace FourOhFour
{
public class MyApplication : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
// Add custom 404 content finder. Use last chance finder so it will return 404 response code correctly...
ContentLastChanceFinderResolver.Current.SetFinder(new FourOhFourContentFinder());
}
}
public class FourOhFourContentFinder : IContentFinder
{
public bool TryFindContent(PublishedContentRequest contentRequest)
{
//Get all root sites...
var allRoots = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetAtRoot();
//Get current site domain...
var domain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
//Get the root node of current site...
var rootNode = allRoots.Where(f => f.UrlWithDomain().StartsWith(domain)).FirstOrDefault();
if (rootNode != null)
{
//Get the site's specific 404 page. Must be called '404 Error Page'...
IPublishedContent notFoundNode = rootNode.Descendants().Where(x => x.Name == "404 Error Page").FirstOrDefault();
if (notFoundNode != null)
{
contentRequest.PublishedContent = notFoundNode;
} else {
//If nothing is found, put in the main site's 404 page...
contentRequest.PublishedContent = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetById(6008);
}
}
return contentRequest.PublishedContent != null;
}
}
}
Well, it's not working now. It works in my test installation, but not on production. I don't know what is different between the two installs.
What is happening is basically it's acting as if I have added no code. It does nothing. No errors, just the ugly Umbraco 404 page, which is what you get when you don't set a default 404 page in the config.
Does anyone have any ideas? I tried replacing this:
I suspect the site needed a restart to pick up the new code.
Touching the web.config or restarting the server would have done this. This happens automatically every few hours too so that might explain why it suddenly started working.
Multi Site 404
My goal is to create a multi site 404 handler. I see several tutorials on how to do this. The problem is that I come from a Web Forms background and am not super familiar with MVC. All the examples say to "create this" and then show code. Where do I save this code? What do I name the file? I feel like I am missing a very simple fundamental step.
Here is the example I am looking at:
https://our.umbraco.com/documentation/reference/routing/request-pipeline/icontentfinder
I don't have an answer for you, but as a primarily front end dev I often get lost there also.
Hi,
The short(ish), not entirely technically correct but hopefully understandable, answer:
There are two basic elements to an MVC site - compiled code and non-compiled "code". Non compiled includes, Views and your front end JS etc - you've found you can happily change this stuff.
As a pure front ender you might be finding code examples which are on the compiled side. This means you can't just drop a .cs file (c#) into the website and it expect it to be picked up (something you might be used to with PHP or similar). It will need to be compiled and forms part of a DLL - this code is picked up when the website starts up. Usually you need to compile, build and release this code.
Here's the good news - you can have code that should really be compiled picked up when the website starts (NOTE: use of this technique should be limited as it can get unwieldy quickly and has MANY downsides!!).
Create a folder in the website directory called App_Code - now if you drop a .cs file in there the website will compile it at startup. This means also if you have some error in your syntax you will also break your website so be careful and don't do this development on a live site!
In fact - if you're getting to this point it's time to start using Visual Studio and building releases, trust me the time in learning this stuff will pay off. This route means that you will be able know on build that you've not broken your site and you'll get lots of hints and tips from Intellisense (think of it as Autocomplete / Autocorrect).
But for now..
Wow, thank you for this. I will give it a go here shortly.
Thank you Steve.
This seems to require the Umbraco.Core.Composing class, which doesn't seem to be available in v7.
It seems the example you provided is meant for v8. I am running v7. I did find this example:
https://24days.in/umbraco-cms/2014/the-double-album/multi-site-404/
but I don't know how to combine them like you did. I get that you have to create a icontenthandler and then have to register it.
Comment deleted - found a bug!
Hi,
I did a quick bit of hacking around in a v7 version. This will work matching on the domain in the URL - if you're doing the same domain with different cultures you'll need to amend it based on the example in the documentation.
It assumes your home nodes are at the root.
But I like this solution as I hope it's more readable and easy to understand.
Be sure to change the fourOhFour alias to match your doctype (ensure you get the case correct too).
Thank you so much! I have been looking for this solution for a while. I had to tweak the code a bit to get it to work on my installation, but not much. I marked your post as the answer. Below is the final result.
Well, it's not working now. It works in my test installation, but not on production. I don't know what is different between the two installs.
What is happening is basically it's acting as if I have added no code. It does nothing. No errors, just the ugly Umbraco 404 page, which is what you get when you don't set a default 404 page in the config.
Does anyone have any ideas? I tried replacing this:
With this:
And nothing changed.
Well, it now seems to be working. I think it was a caching issue. Thanks again to Steve for helping me solve this one!
I suspect the site needed a restart to pick up the new code.
Touching the web.config or restarting the server would have done this. This happens automatically every few hours too so that might explain why it suddenly started working.
HTH
Steve
is working on a reply...