Hello all,
I am trying to setup a 404 on the CWS Start package site I am working on, but it feels a bit weird to hardcode a node ID in the umbracosettings.config file.
What if I delete the node and then recreate it, the node ID will have changed.
My thinking is that it should detect from a nodetype alias or a checkbox or something similar.
I want to open this up to a wider discussion to see what other people use as alternatives and what other options are available to me.
In the past where pre-installing ucomponents hasn't been an option, I've used the umbraco 404 handler, which is managed in the 404 handlers.config. This is actually how uComponents deals with it, but if you're going to be using it with CWS and not want to force uComponents, you may feel like writing your own extension. There's a pretty good wiki page which I used when I wrote ours.
On our multi-sites, multi-languages platform we can't have one unique node ID to be the global 404 page. We want one 404 page per website, and even one per language. By convention we have decided that the 404 pages will be named "404" (in the tree) and we have a special NotFoundHandler that locates the page named "404" anywhere under the current domain root.
I realize it would be nicer to use a dedicated content type... so if I were to turn this into a feature, I'd say the 404 pages content type should go in a config file, and then the handler tries to find a page of that type under the current domain root.
Not sure I like the checkbox idea because it means that every document will get a new property which will be almost never used... and if you create a special content type for that purpose, then, well, you're back to my previous idea.
Hey Stephen,
I am thinking of going down the dedicated document type route then the nodename can be whatever rather than trying to detect if it's called 404
But lets keep the discussion flowing, so is the only way to role your own 404 handler & is it possible to add a data attribute to the class for it to be automatically registered, rather than updating the 404 handlers config file?
+1 for implementing your own INotFoundHandler. If your Umbraco install only has one site, you can grab the root node and then get the first child named "404", the first child with a document type alias of "NotFoundPage" or whatever logic suits you. If you run a multiple site setup, grab the context host name eg.:
var server = HttpContext.Current.Request.ServerVariables["SERVER_NAME"].ToLower();
And then use domains to get the root id for the context site eg.:
var rootNodeId = Domain.GetRootFromDomain(server);
Then use that id to search for your 404 page, again, using your search logic of choice.
For v6.1+, I'd go with the new IContentFinder API.
Hook it up in app-startup:
public class MyApplication : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
ContentFinderResolver.Current.InsertTypeBefore<ContentFinderByNotFoundHandlers, ContentFinderForWhatever>();
base.ApplicationStarting(umbracoApplication, applicationContext);
}
}
Then implement the interface.
public class ContentFinderForWhatever : IContentFinder
{
public bool TryFindContent(PublishedContentRequest contentRequest)
{
if (contentRequest != null)
{
// do something with 'contentRequest' to find an appropriate 404 node
// contentRequest.PublishedContent = node;
// then set the HTTP Status Code to '404'
}
return contentRequest.PublishedContent != null;
}
}
I believe the the IContentFinder runs upfront (as part of the new pipeline), whereas the older NotFoundHandlers would run afterwards.
@Warren if you're targetting 6.1+ then you don't want a NotFoundHandler but an IContentFinder. Just register it as the last of the IContentFinder, or even better, as the "last chance" content finder (see ContentLastChanceFinderResolver).
@Lee +1 for the IContentFinder, though if it's your true 404 finder and you always want to run it last, it's better to register it as the "last chance" finder via the ContentLastChanceFinderResolver. That's because the "last chance" one will also be used to handle special cases such as documents with no template, things like that.
@Mads in the IContentFinder.TryFindContent() method, the PublishedContentRequest already has a Domain property that will give you the domain that was detected by the pipeline, no need to do it all again by yourself!
Awesome post Lee. Do you know if the new pipelines and features will be documented anywhere for us mere mortals? It'd be good to get an overview and not really sure where to keep abreast of this sort of stuff - other than here of course.
@Warren: you don't need to implement the resolver. You create an IContentFinder either by subclassing an existing one or by implementing IContentFinder (see @Lee's examples), say WarrenContentFinder. Then you create an application event handler and do something like
An option of replacing the ids with an xpath like some of uComponents do, isn't bad though. Maybe even combined with query/path variables from the request.
For anyone interested here is how I have approached it thanks to Lee K & Stefan using the Request Pipeline
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;
}
}
Then the Application Starting event
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());
}
@Niels - +1, having an XPath option would make it much more powerful out of the box!
@Warren - the returning line return contentRequest.PublishedContent != null; just lets the method know that it has found a published content node. (I took that in turn from one of Stephan's IContentFinder in the core) :-)
This way when an exception occurs it won't get redirect to /500?aspxerrorpath=/some/page
The /404 and /500 URLs are just plain umbraco documents with the umbracoUrlAlias set.
@Warren - the content finder needs to return true to signal it has found a document. That's what
return contentRequest.PublishedContent != null;
does. But if you know what you're doing you can just return true.
Also if contentRequest.Is404 is true, then I think you don't need to SetResponseStatus(404)... should be done automatically... I think (not 100% sure).
@All - I like the idea of the XPath query. Using a hard-coded ID, or Warren's method (XPath "/CWS-Home//CWS-404"), can be expressed with XPath. The default 404 handler could use XPath. Though we'd need a built-in variable to represent the current domain root... or decide that, when a domain root was found, the XPath is relative to that root.
I implemented Warren's code but I still see the old ugly 404 page.
When I hit the debugger I see the ContentFinder being executed. I'm also doing route hijacking and I see the controller index method being hit for my 404 page.
Also from the logs I see that the finder is being executed :
2014-02-27 10:50:29,958 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Replace handler 'umbraco.SearchForAlias' by new finder 'Umbraco.Web.Routing.ContentFinderByUrlAlias'.
2014-02-27 10:50:29,966 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Replace handler 'umbraco.SearchForTemplate' by new finder 'Umbraco.Web.Routing.ContentFinderByNiceUrlAndTemplate'.
2014-02-27 10:50:29,986 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrlAndTemplate - [Thread 61] Not a valid template: "t"
2014-02-27 10:50:29,986 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Replace handler 'umbraco.SearchForProfile' by new finder 'Umbraco.Web.Routing.ContentFinderByProfile'.
2014-02-27 10:50:29,989 [61] DEBUG Umbraco.Web.Routing.ContentFinderByProfile - [Thread 61] Not the profile path
2014-02-27 10:50:30,000 [61] WARN Umbraco.Web.Routing.DefaultUrlProvider - [Thread 61] Couldn't find any page with nodeId=-1. This is most likely caused by the page not being published.
2014-02-27 10:50:30,321 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindPublishedContent: End finders, no document was found (took 438ms)
2014-02-27 10:50:30,321 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: Begin
2014-02-27 10:50:30,321 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: No document, try last chance lookup
2014-02-27 10:50:30,342 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: Found a document
2014-02-27 10:50:30,343 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] EnsurePublishedContentAccess: Page is not protected
2014-02-27 10:50:30,343 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: End
So you're seeing the ugly 404 page? According to the log you should see page ID 1351 displayed with template ID 1071 in "404" mode unless your controller does something strange in between?
Crazy. So the view is executed but what you see is the ugly 404 page?! Any chance you can extract a complete stack trace when hitting a breakpoint within the view?
Stack trace from controller just before returning view :
>NederlandSchoon.Webshop.Controllers.dll!NederlandSchoon.Webshop.Controllers.RenderMvcController.TextPageController.Index(Umbraco.Web.Models.RenderModel model) Line 72C#
- is the IContentFinder running as a normal finder or as the last chance finder? - does the IContentFinder return true to indicate it found content? - care to share a bit of controller code so I can see what it does? - have you tried to repro on 6.2?
- have you tried to repro on 6.2? -- No I'm still on 6.1.6. It's a big project so upgrading to 6.2 won't happen until it's final because upgrading takes a lot of time.
Dropped the following code in App_Code on a 6.2.0 install. DebugFinder returns a hard-coded document of type "Document". It's using your BaseSurfaceController (with a few things removed so it compiles but should not make a diff).
public class DebugFinder : IContentFinder { public bool TryFindContent(PublishedContentRequest contentRequest) { var content = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetById(1069); if (content != null) contentRequest.PublishedContent = content; return content != null; } }
public class MyApplication : ApplicationEventHandler { protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { base.ApplicationStarting(umbracoApplication, applicationContext);
public class DocumentController : BaseSurfaceController { public override ActionResult Index(RenderModel model) { ControllerContext.HttpContext.Response.Write("***"); var baseModel = GetModel<BaseModel>(); return CurrentTemplate(model); } }
And I do see the document being rendered with its template... and the "***" at the top of the page indicating that the controller ran. So I'd say it works or did I miss something? Can you try that simple code (or something quite similar) on 6.1 so we know we're talking about the same thing?
Also... when your finder & controller mess with the document... the final document does have a template, right?
We must have been thinking the same thing because right before I saw your post I already tried to replicate my situation in the latest 6.2.0 nightly :-).
I also tried your example in 6.2.0 and it works there and I copied that same example to my 6.1.6 install and it doesn't work there. Of course my 6.1.6. runs on the Hybrid Framework and it might be something else, but it's hard to be sure. It doesn't seem to be related to route hijacking and I also tried Webforms and MVC in 6.2.0.
I think I'm also going to try it in a clean 6.1.6 install and use the same code as for 6.2.0. If it doesn't work there we know it's fixed for 6.2.0 :-).
I just tried your code example in the latest 6.2.0 nightly and 6.1.6 and it works in both versions. So it must be something else. I'll do some experiments with domains because both test version don't have domains and my website does.
Strange thing is that I also can't seem to enable the old 404 way. I removed the NotFoundContentFinder from OnApplicationStarting and just set an id in the umbracoSettings.config and that also doesn't work. I've had other websites with the Hybrid Framework in which the old 404 did work. Some kind of setting is messing things up...
Ok it was definitely a setting and I finally found out what it was. Both the NotFoundContentFinder and old 404 didn't work because I have installed SEO Checker and it overwrites the 404 settings. So it doesn't matter what you do it will only listen to SEO Checker. In there you can set a 404 per domain and language (Would be nice if parent node could also be displayed in the node name):
It a flexible way of doing things, but I hope the SEO Checker 404 can be disabled because with my NotFoundContentFinder I can do the same, but programmatically so I don't need to set a 404 each time I create a new website and domain (which will happen a lot for this project).
My problem had the same cause. Also SEO checker that got in the way, but I didn't know I could set the 404 page with SEO checker. Maybe Richard can tell you how to solve your problem, because I know it's possible to create a provider for redirects so you don't have to set them manually.
Glad to know at least we know what's going on. As for SEO checker... if it takes over 404 then it must register a "not found" handler in 404handlers.config? Or in some place? Don't think it uses IContentFinder at the moment. Richard should be able to tell you?
Not sure if you all managed to resolve this. I wrote a custom 404 Page finder due to the fact that I have multiple sites running in a single install.
It worked perfectly on my local machine, and failed on my dev server. Changing the "trySkipIisCustomErrors" setting to true in Umbraco.config allowed it to work there. However, it failed on our production server with that setting changed, and works with it as default.
The dev server and our production server do run different versions of IIS.
Sorry guys for the late reply was on Holiday and on-site last week. SEO Checker uses a HTTPModule then it can be used for any request. But it only kicks in when the http status of the request is 404 and is really easy to configure for webmasters :). But I see that it is giving some issues on some sites so I will make an option to disable it. Working on a Patch release this week so expect it to be release soon.
just FYI I've found the same issue with the latest Umbraco (v8.5.4) and SEO Checker. When I wanted to handle my custom page for 404 using the LastChanceContentFinder - the SEO Checker module replaced the request and prevented me from serving it correctly.
The behaviour was odd when I debugged it as it replaced the PublishedContent object as I wanted, but still served the "ugly page" even if all the other finders were disabled/removed.
Just letting you know as I don't know if you placed the setting to control this behaviour or not. Might need it if client will decide to use SEO checker on this page :)
It's too bad we can only have one LastChanceContentFinder ;-) Thing is SEOChecker needs some context as well for multi lingual purposes. I think for 2.10 I will revert this and use the End_Request again. Need to determine language context anyway for different parts of SEOChecker in that version.
Alternatives to 404 in umbracosettings.config?
Hello all,
I am trying to setup a 404 on the CWS Start package site I am working on, but it feels a bit weird to hardcode a node ID in the umbracosettings.config file.
What if I delete the node and then recreate it, the node ID will have changed. My thinking is that it should detect from a nodetype alias or a checkbox or something similar.
I want to open this up to a wider discussion to see what other people use as alternatives and what other options are available to me.
Thanks
Warren
Hey Warren,
In the past where pre-installing ucomponents hasn't been an option, I've used the umbraco 404 handler, which is managed in the 404 handlers.config. This is actually how uComponents deals with it, but if you're going to be using it with CWS and not want to force uComponents, you may feel like writing your own extension. There's a pretty good wiki page which I used when I wrote ours.
http://our.umbraco.org/wiki/how-tos/how-to-implement-your-own-404-handler
Looking forward to trying out the CWS - I'll give it a go when I have a sec and let you know how it goes.
thanks,
On our multi-sites, multi-languages platform we can't have one unique node ID to be the global 404 page. We want one 404 page per website, and even one per language. By convention we have decided that the 404 pages will be named "404" (in the tree) and we have a special NotFoundHandler that locates the page named "404" anywhere under the current domain root.
I realize it would be nicer to use a dedicated content type... so if I were to turn this into a feature, I'd say the 404 pages content type should go in a config file, and then the handler tries to find a page of that type under the current domain root.
Not sure I like the checkbox idea because it means that every document will get a new property which will be almost never used... and if you create a special content type for that purpose, then, well, you're back to my previous idea.
Hey Stephen,
I am thinking of going down the dedicated document type route then the nodename can be whatever rather than trying to detect if it's called 404
But lets keep the discussion flowing, so is the only way to role your own 404 handler & is it possible to add a data attribute to the class for it to be automatically registered, rather than updating the 404 handlers config file?
Cheers,
Warren
+1 for implementing your own
INotFoundHandler
. If your Umbraco install only has one site, you can grab the root node and then get the first child named "404", the first child with a document type alias of "NotFoundPage" or whatever logic suits you. If you run a multiple site setup, grab the context host name eg.:And then use domains to get the root id for the context site eg.:
Then use that id to search for your 404 page, again, using your search logic of choice.
For v6.1+, I'd go with the new
IContentFinder
API.Hook it up in app-startup:
Then implement the interface.
I believe the the
IContentFinder
runs upfront (as part of the new pipeline), whereas the olderNotFoundHandlers
would run afterwards.Cheers,
- Lee
Great suggestion Lee, thanks, I'll grab that one myself! :)
Actually, I think I remember Stephen talking about that at #cg2013 ?
@Warren if you're targetting 6.1+ then you don't want a NotFoundHandler but an IContentFinder. Just register it as the last of the IContentFinder, or even better, as the "last chance" content finder (see ContentLastChanceFinderResolver).
@Lee +1 for the IContentFinder, though if it's your true 404 finder and you always want to run it last, it's better to register it as the "last chance" finder via the ContentLastChanceFinderResolver. That's because the "last chance" one will also be used to handle special cases such as documents with no template, things like that.
@Mads in the IContentFinder.TryFindContent() method, the PublishedContentRequest already has a Domain property that will give you the domain that was detected by the pipeline, no need to do it all again by yourself!
Stephan
@Lee yay something that I don't need to register in a .config file that may be the route I go for this then.
Not heard of the IContentFinder before, got any more details on how it works and when its run in the request pipeline please?
Thanks,
Warren
@Warren - sure you hadn't heard of it before? ;-) https://gist.github.com/leekelleher/5966488
@Stephan - Good point about the
ContentLastChanceFinderResolver
- thanks for the point! :-)Awesome post Lee. Do you know if the new pipelines and features will be documented anywhere for us mere mortals? It'd be good to get an overview and not really sure where to keep abreast of this sort of stuff - other than here of course.
@Stephen yes I am targeting 6.1+ so no need for the 404 handler stuff then.
Are you able to provide a quick snippet or example on how to use it please, be it on here or on a Gist or something.
Thanks
Warren
@Stephen That is VERY cool! I'm liking this new api more and more :D #h5yr !!
@Lee hahaha I have a terrible memory, barely remember what I had for breakfast this morning let alone a comment on some Gist a while back :-P
Re. documentation, best I can do right now is to point you to: http://zpqrtbnk.net/TheUmbraco6RequestPipeline.pdf
Hopefully will have time to write more in a couple of weeks.
@Stephen that's fantastic. Much appreciated
@stephen that PDF is great, some of it a little over my head for this time of the morning for me.
Sorry to be a pain so how do I implement the ContentLastChanceFinderResolver?
Thanks
Warren
@Warren: you don't need to implement the resolver. You create an IContentFinder either by subclassing an existing one or by implementing IContentFinder (see @Lee's examples), say WarrenContentFinder. Then you create an application event handler and do something like
An option of replacing the ids with an xpath like some of uComponents do, isn't bad though. Maybe even combined with query/path variables from the request.
@Stephen thanks. You may want to edit your post as you accidently forgot the .Current
For anyone interested here is how I have approached it thanks to Lee K & Stefan using the Request Pipeline
Then the Application Starting event
@Niels - +1, having an XPath option would make it much more powerful out of the box!
@Warren - the returning line
return contentRequest.PublishedContent != null;
just lets the method know that it has found a published content node. (I took that in turn from one of Stephan'sIContentFinder
in the core) :-)@Lee thanks for that, as I wasn't 100% sure what it was returning/doing but makes sense now :)
@Niels yes an xPath to the 404 node would be much nicer than a hardcoded NodeID in the config file
+1 for this thread.
Everytime I enter the nodeID, I think to myself, this should be an XPATH :)
$currentPage/ancestor-or-self::Home/descendant::Error
or
$currentPage/ancestor-or-self::Home/descendant::Error[@name = '404']
@Lau - I'd like this too. The problem is what would be the context of
$currentPage
? As the page/node can't be found :-$I haven't read all the replies above, just wanted to share how I'm handling 404 Not Founds and 500 Errors.
I disable the umbraco404 NotFound handler in 404handlers.config.
Then I set to Off in .
After that I add this to the
This way when an exception occurs it won't get redirect to /500?aspxerrorpath=/some/page
The /404 and /500 URLs are just plain umbraco documents with the umbracoUrlAlias set.
@Warren - the content finder needs to return true to signal it has found a document. That's what
does. But if you know what you're doing you can just return true.
Also if contentRequest.Is404 is true, then I think you don't need to SetResponseStatus(404)... should be done automatically... I think (not 100% sure).
@All - I like the idea of the XPath query. Using a hard-coded ID, or Warren's method (XPath "/CWS-Home//CWS-404"), can be expressed with XPath. The default 404 handler could use XPath. Though we'd need a built-in variable to represent the current domain root... or decide that, when a domain root was found, the XPath is relative to that root.
Will try to prototype such a 404 content finder.
Stephan
@Stephan I look forward to see what you do/build for a new 404 handler.
Hi all,
I implemented Warren's code but I still see the old ugly 404 page.
When I hit the debugger I see the ContentFinder being executed. I'm also doing route hijacking and I see the controller index method being hit for my 404 page.
Also from the logs I see that the finder is being executed :
2014-02-27 10:50:29,879 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindDomain: Uri="http://localhost/t"
2014-02-27 10:50:29,880 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindDomain: Matches no domain
2014-02-27 10:50:29,880 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindDomain: Culture="nl-NL"
2014-02-27 10:50:29,881 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindPublishedContentAndTemplate: Path="/t"
2014-02-27 10:50:29,881 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindPublishedContent: Begin finders
2014-02-27 10:50:29,926 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrl - [Thread 61] Test route "/t"
2014-02-27 10:50:29,926 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrl - [Thread 61] No match.
2014-02-27 10:50:29,929 [61] DEBUG Umbraco.Web.Routing.ContentFinderByIdPath - [Thread 61] Not a node id
2014-02-27 10:50:29,944 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Registering custom handlers.
2014-02-27 10:50:29,945 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Configured: 'umbraco.SearchForAlias,umbraco'.
2014-02-27 10:50:29,946 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Registering 'umbraco.SearchForAlias'.
2014-02-27 10:50:29,947 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Configured: 'umbraco.SearchForTemplate,umbraco'.
2014-02-27 10:50:29,947 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Registering 'umbraco.SearchForTemplate'.
2014-02-27 10:50:29,948 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Configured: 'umbraco.SearchForProfile,umbraco'.
2014-02-27 10:50:29,948 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Registering 'umbraco.SearchForProfile'.
2014-02-27 10:50:29,948 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Configured: 'SEOChecker.Handlers.NotFoundHandlers.Inbound404Handler,SEOChecker'.
2014-02-27 10:50:29,949 [61] DEBUG Umbraco.Web.Routing.NotFoundHandlerHelper - [Thread 61] Registering 'SEOChecker.Handlers.NotFoundHandlers.Inbound404Handler'.
2014-02-27 10:50:29,953 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Running for legacy url='t'.
2014-02-27 10:50:29,957 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Handler 'umbraco.SearchForAlias'.
2014-02-27 10:50:29,958 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Replace handler 'umbraco.SearchForAlias' by new finder 'Umbraco.Web.Routing.ContentFinderByUrlAlias'.
2014-02-27 10:50:29,966 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Handler 'umbraco.SearchForTemplate'.
2014-02-27 10:50:29,966 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Replace handler 'umbraco.SearchForTemplate' by new finder 'Umbraco.Web.Routing.ContentFinderByNiceUrlAndTemplate'.
2014-02-27 10:50:29,986 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrlAndTemplate - [Thread 61] Not a valid template: "t"
2014-02-27 10:50:29,986 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Handler 'umbraco.SearchForProfile'.
2014-02-27 10:50:29,986 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Replace handler 'umbraco.SearchForProfile' by new finder 'Umbraco.Web.Routing.ContentFinderByProfile'.
2014-02-27 10:50:29,989 [61] DEBUG Umbraco.Web.Routing.ContentFinderByProfile - [Thread 61] Not the profile path
2014-02-27 10:50:29,990 [61] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - [Thread 61] Handler 'SEOChecker.Handlers.NotFoundHandlers.Inbound404Handler'.
2014-02-27 10:50:30,000 [61] WARN Umbraco.Web.Routing.DefaultUrlProvider - [Thread 61] Couldn't find any page with nodeId=-1. This is most likely caused by the page not being published.
2014-02-27 10:50:30,321 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindPublishedContent: End finders, no document was found (took 438ms)
2014-02-27 10:50:30,321 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: Begin
2014-02-27 10:50:30,321 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: No document, try last chance lookup
2014-02-27 10:50:30,342 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: Found a document
2014-02-27 10:50:30,343 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] EnsurePublishedContentAccess: Page is not protected
2014-02-27 10:50:30,343 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandlePublishedContent: End
2014-02-27 10:50:30,344 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindTemplate: Look for template id=1071
2014-02-27 10:50:30,348 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindTemplate: Got template id=1071 alias="TextPage"
2014-02-27 10:50:30,348 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] FindTemplate: Running with template id=1071 alias="TextPage"
2014-02-27 10:50:30,348 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandleWildcardDomains: Path="-1,1074,1351"
2014-02-27 10:50:30,348 [61] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - [Thread 61] HandleWildcardDomains: No match.
2014-02-27 10:50:30,349 [61] DEBUG Umbraco.Web.UmbracoModule - [Thread 61] Response status: Redirect=none, Is404=true, StatusCode=404
2014-02-27 10:50:31,994 [61] DEBUG Umbraco.Web.UmbracoModule - [Thread 61] Total milliseconds for umbraco request to process: 2115,5529
Any ideas what I'm doing wrong ?
Anyone ?
So you're seeing the ugly 404 page? According to the log you should see page ID 1351 displayed with template ID 1071 in "404" mode unless your controller does something strange in between?
Hi Stephen,
My controller just shows the view
like this
return this.View("Viewname", model)
And is this method ever executed? Does the view "viewname" exists?
Yep...View exits and is executed..breakpoints in view are hit.
Crazy. So the view is executed but what you see is the ugly 404 page?! Any chance you can extract a complete stack trace when hitting a breakpoint within the view?
Stack trace from controller just before returning view :
>NederlandSchoon.Webshop.Controllers.dll!NederlandSchoon.Webshop.Controllers.RenderMvcController.TextPageController.Index(Umbraco.Web.Models.RenderModel model) Line 72C#
[Lightweight Function]
System.Web.Mvc.dll!System.Web.Mvc.ActionMethodDispatcher.Execute(System.Web.Mvc.ControllerBase controller, object[] parameters) + 0xf bytes
System.Web.Mvc.dll!System.Web.Mvc.ReflectedActionDescriptor.Execute(System.Web.Mvc.ControllerContext controllerContext, System.Collections.Generic.IDictionaryparameters) + 0xd4 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ActionDescriptor actionDescriptor, System.Collections.Generic.IDictionaryparameters) + 0x1c bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters.AnonymousMethod__10() + 0x38 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(System.Web.Mvc.IActionFilter filter, System.Web.Mvc.ActionExecutingContext preContext, System.Funccontinuation) + 0x77 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters.AnonymousMethod__12() + 0x16 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(System.Web.Mvc.IActionFilter filter, System.Web.Mvc.ActionExecutingContext preContext, System.Funccontinuation) + 0x77 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters.AnonymousMethod__12() + 0x16 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(System.Web.Mvc.IActionFilter filter, System.Web.Mvc.ActionExecutingContext preContext, System.Funccontinuation) + 0x77 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters.AnonymousMethod__12() + 0x16 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(System.Web.Mvc.ControllerContext controllerContext, System.Collections.Generic.IListfilters, System.Web.Mvc.ActionDescriptor actionDescriptor, System.Collections.Generic.IDictionaryparameters) + 0xc0 bytes
System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeAction(System.Web.Mvc.ControllerContext controllerContext, string actionName) + 0xe8 bytes
System.Web.Mvc.dll!System.Web.Mvc.Controller.BeginExecuteCore.AnonymousMethod__19() + 0x18 bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.MakeVoidDelegate.AnonymousMethod__0() + 0x14 bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.BeginSynchronous.AnonymousMethod__7(System.IAsyncResult _) + 0xb bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult.End() + 0x3f bytes
System.Web.Mvc.dll!System.Web.Mvc.Controller.EndExecuteCore(System.IAsyncResult asyncResult) + 0x3a bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.MakeVoidDelegate.AnonymousMethod__3(System.IAsyncResult ar) + 0x18 bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult.End() + 0x3f bytes
System.Web.Mvc.dll!System.Web.Mvc.Controller.EndExecute(System.IAsyncResult asyncResult) + 0x30 bytes
System.Web.Mvc.dll!System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(System.IAsyncResult asyncResult) + 0xb bytes
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.BeginProcessRequest.AnonymousMethod__3(System.IAsyncResult asyncResult) + 0x1a bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.MakeVoidDelegate.AnonymousMethod__3(System.IAsyncResult ar) + 0x18 bytes
System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult.End() + 0x3f bytes
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.EndProcessRequest(System.IAsyncResult asyncResult) + 0x30 bytes
System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(System.IAsyncResult result) + 0xa bytes
System.Web.dll!System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() + 0x242 bytes
System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step, ref bool completedSynchronously) + 0x9c bytes
System.Web.dll!System.Web.HttpApplication.PipelineStepManager.ResumeSteps(System.Exception error) + 0x43d bytes
System.Web.dll!System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext context, System.AsyncCallback cb) + 0x60 bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest wr, System.Web.HttpContext context) + 0xbb bytes
System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x2d7 bytes
System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x1f bytes
[Native to Managed Transition]
[Managed to Native Transition]
System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x456 bytes
System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x1f bytes
[Appdomain Transition]
Thanks. The plot thickens. So your view executes correctly... I need to look deeper into it. Bit busy at the moment, later today hopefully!
Thanks...if I find time I will use the Umbraco Source from 6.1.6 to see if I can find the problem. Maybe you can give me some pointers where to look ?
Hello,
I'm having the exact same problem as dawoe. I'm on Umbraco 6.1.6.
- The IContentFinder is hit and finds a 404 node that is set on contentRequest.PublishedContent.
- Than is goes to my controller through route hijacking and it returns the correct view and model.
- After that I expect that node to be displayed, but I'm also getting the old ugly 404 page.
Is there any solution to this?
Jeroen
@Jeroen:
- is the IContentFinder running as a normal finder or as the last chance finder?
- does the IContentFinder return true to indicate it found content?
- care to share a bit of controller code so I can see what it does?
- have you tried to repro on 6.2?
- is the IContentFinder running as a normal finder or as the last chance finder?
-- It's the last change finder from Warren his example.
- does the IContentFinder return true to indicate it found content?
-- Yes after the node is set it returns contentRequest.PublishedContent != null.
- care to share a bit of controller code so I can see what it does?
-- Currently just a default controller which doesn't do much. Like in the Hybrid Framework: https://github.com/jbreuer/Hybrid-Framework-Best-Practices/blob/master/development/Umbraco.Extensions/Controllers/DefaultController.cs. I also tested without route hijacking and I'm having the problem.
- have you tried to repro on 6.2?
-- No I'm still on 6.1.6. It's a big project so upgrading to 6.2 won't happen until it's final because upgrading takes a lot of time.
Jeroen
Dropped the following code in App_Code on a 6.2.0 install. DebugFinder returns a hard-coded document of type "Document". It's using your BaseSurfaceController (with a few things removed so it compiles but should not make a diff).
And I do see the document being rendered with its template... and the "***" at the top of the page indicating that the controller ran. So I'd say it works or did I miss something? Can you try that simple code (or something quite similar) on 6.1 so we know we're talking about the same thing?
Also... when your finder & controller mess with the document... the final document does have a template, right?
Stephan
We must have been thinking the same thing because right before I saw your post I already tried to replicate my situation in the latest 6.2.0 nightly :-).
I also tried your example in 6.2.0 and it works there and I copied that same example to my 6.1.6 install and it doesn't work there. Of course my 6.1.6. runs on the Hybrid Framework and it might be something else, but it's hard to be sure. It doesn't seem to be related to route hijacking and I also tried Webforms and MVC in 6.2.0.
I think I'm also going to try it in a clean 6.1.6 install and use the same code as for 6.2.0. If it doesn't work there we know it's fixed for 6.2.0 :-).
Thanks for helping.
Jeroen
I just tried your code example in the latest 6.2.0 nightly and 6.1.6 and it works in both versions. So it must be something else. I'll do some experiments with domains because both test version don't have domains and my website does.
Jeroen
I really tried some different scenarios.
- Single website
- Single website with domain
- Single website with domain and useDomainPrefixes set to true
- Multiple websites with each a unique domain and useDomainPrefixes set to true
They all seem to work in 6.1.6. Even with the default controller enabled.
I could send the code of my current website if you have to time to take a look at it. Really strange that it's not working in my website.
Jeroen
Strange thing is that I also can't seem to enable the old 404 way. I removed the NotFoundContentFinder from OnApplicationStarting and just set an id in the umbracoSettings.config and that also doesn't work. I've had other websites with the Hybrid Framework in which the old 404 did work. Some kind of setting is messing things up...
Jeroen
Ok it was definitely a setting and I finally found out what it was. Both the NotFoundContentFinder and old 404 didn't work because I have installed SEO Checker and it overwrites the 404 settings. So it doesn't matter what you do it will only listen to SEO Checker. In there you can set a 404 per domain and language (Would be nice if parent node could also be displayed in the node name):
It a flexible way of doing things, but I hope the SEO Checker 404 can be disabled because with my NotFoundContentFinder I can do the same, but programmatically so I don't need to set a 404 each time I create a new website and domain (which will happen a lot for this project).
Jeroen
Hey Jeroen,
My problem had the same cause. Also SEO checker that got in the way, but I didn't know I could set the 404 page with SEO checker. Maybe Richard can tell you how to solve your problem, because I know it's possible to create a provider for redirects so you don't have to set them manually.
Dave
Glad to know at least we know what's going on. As for SEO checker... if it takes over 404 then it must register a "not found" handler in 404handlers.config? Or in some place? Don't think it uses IContentFinder at the moment. Richard should be able to tell you?
Not sure if you all managed to resolve this. I wrote a custom 404 Page finder due to the fact that I have multiple sites running in a single install.
It worked perfectly on my local machine, and failed on my dev server. Changing the "trySkipIisCustomErrors" setting to true in Umbraco.config allowed it to work there. However, it failed on our production server with that setting changed, and works with it as default.
The dev server and our production server do run different versions of IIS.
Here is my code for the contentfinder...
Sorry guys for the late reply was on Holiday and on-site last week. SEO Checker uses a HTTPModule then it can be used for any request. But it only kicks in when the http status of the request is 404 and is really easy to configure for webmasters :). But I see that it is giving some issues on some sites so I will make an option to disable it. Working on a Patch release this week so expect it to be release soon.
Sorry if it caused you some issues.
Best,
Richard
Hi Richard,
just FYI I've found the same issue with the latest Umbraco (v8.5.4) and SEO Checker. When I wanted to handle my custom page for 404 using the LastChanceContentFinder - the SEO Checker module replaced the request and prevented me from serving it correctly.
The behaviour was odd when I debugged it as it replaced the PublishedContent object as I wanted, but still served the "ugly page" even if all the other finders were disabled/removed.
Just letting you know as I don't know if you placed the setting to control this behaviour or not. Might need it if client will decide to use SEO checker on this page :)
Cheers, Marcin
Hi @all,
I know that this is a very old topic but google still bring this in first 5 no matter what I search :) .
As you know, the x-path way has been implemented. I've tested it and it works fine if there is only one site. How shall I set it for multiple sites?
I put this tags in my umbracoSettings.config which will find the first 404 page in all my sites.
How can I set it to find the 404 page for each domain?
I've tested this one too, but it only brings the ugly 404 page back
Hi Ashkan,
I answered your question on SO: http://stackoverflow.com/a/39578544/1029339 :)
Hi Marcin,
It's too bad we can only have one LastChanceContentFinder ;-) Thing is SEOChecker needs some context as well for multi lingual purposes. I think for 2.10 I will revert this and use the End_Request again. Need to determine language context anyway for different parts of SEOChecker in that version.
Cheers,
Richard
Gotcha! Isn't it possible to control the registration of it by using some fixed order of registrations (or even custom order entry for SEO Checker)?
Maybe then we can set up our custom one After(ISeoCheckerComponent) or something like it?
That's one of the thing's I want to check. From the top of my head it gave some conflicts with the default LastChanceContentFinder.
to be continued ;-)
is working on a reply...