Currently we have problem, that when editor creates new node, e.g. journey-chapter-3 and then assigns vrt-1 to it, url http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1 returns 404. While debugging I see, that it's correctly resolved by our custom UmbracoVirtualNodeByIdRouteHandler But it doesn't go further to controller. Also strange thing found in logs, that it makes 2 requests to the page:
2015-10-08 14:33:01,758 [P19400/D2/T147] DEBUG Umbraco.Web.UmbracoModule - Begin request: http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1.
2015-10-08 14:33:01,760 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindDomain: Uri="http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1"
2015-10-08 14:33:01,761 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindDomain: Matches domain="dev.Demoportal.com/nl", rootId=1144, culture="nl-NL"
2015-10-08 14:33:01,761 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindDomain: Culture="nl-NL"
2015-10-08 14:33:01,761 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - HandleWildcardDomains: Path="-1,1143,1164,1165,1322,1323"
2015-10-08 14:33:01,761 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - HandleWildcardDomains: No match.
2015-10-08 14:33:01,762 [P19400/D2/T147] DEBUG Umbraco.Core.Sync.DatabaseServerMessenger - Syncing from database...
2015-10-08 14:33:02,037 [P19400/D2/T147] DEBUG Umbraco.Core.Sync.DatabaseServerMessenger - Complete (took 274ms)
2015-10-08 14:33:02,555 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindDomain: Uri="http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1"
2015-10-08 14:33:02,556 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindDomain: Matches domain="dev.Demoportal.com/nl", rootId=1144, culture="nl-NL"
2015-10-08 14:33:02,556 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindDomain: Culture="nl-NL"
2015-10-08 14:33:02,556 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindPublishedContentAndTemplate: Path="/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1"
2015-10-08 14:33:02,556 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindPublishedContent: Begin finders
2015-10-08 14:33:02,557 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrl - Test route "1144/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1"
2015-10-08 14:33:02,557 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrl - No match.
2015-10-08 14:33:02,557 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByIdPath - Not a node id
2015-10-08 14:33:02,557 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Running for legacy url='nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1'.
2015-10-08 14:33:02,557 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Handler 'umbraco.SearchForAlias'.
2015-10-08 14:33:02,557 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Replace handler 'umbraco.SearchForAlias' by new finder 'Umbraco.Web.Routing.ContentFinderByUrlAlias'.
2015-10-08 14:33:02,558 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Handler 'umbraco.SearchForTemplate'.
2015-10-08 14:33:02,558 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Replace handler 'umbraco.SearchForTemplate' by new finder 'Umbraco.Web.Routing.ContentFinderByNiceUrlAndTemplate'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNiceUrlAndTemplate - Not a valid template: "vrt-1"
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Handler 'umbraco.SearchForProfile'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Replace handler 'umbraco.SearchForProfile' by new finder 'Umbraco.Web.Routing.ContentFinderByProfile'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByProfile - Not the profile path
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByNotFoundHandlers - Handler 'SEOChecker.Handlers.NotFoundHandlers.Inbound404Handler'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - FindPublishedContent: End finders, no document was found (took 193ms)
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - HandlePublishedContent: Begin
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - HandlePublishedContent: No document, try last chance lookup
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentLastChanceFinderByNotFoundHandlers - Running for legacy url='nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentLastChanceFinderByNotFoundHandlers - Handler 'umbraco.handle404'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentLastChanceFinderByNotFoundHandlers - Replace handler 'umbraco.handle404' by new finder 'Umbraco.Web.Routing.ContentFinderByLegacy404'.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByLegacy404 - Looking for a page to handle 404.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByLegacy404 - Got id=1.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.ContentFinderByLegacy404 - Could not find content with that id.
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - HandlePublishedContent: Failed to find a document, give up
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.Routing.PublishedContentRequestEngine - HandlePublishedContent: End
2015-10-08 14:33:02,750 [P19400/D2/T147] DEBUG Umbraco.Web.UmbracoModule - Response status: Redirect=none, Is404=true, StatusCode=0
2015-10-08 14:33:02,751 [P19400/D2/T147] WARN Umbraco.Web.UmbracoModule - Status code is 404 yet TrySkipIisCustomErrors is false - IIS will take over.
2015-10-08 14:33:02,752 [P19400/D2/T147] DEBUG Umbraco.Web.UmbracoModule - Total milliseconds for umbraco request to process: 993.9929
Then after some time (no actions are taken) it starts working correctly and makes only 1 request to page:
The problem with that is that there is no guarantee that the cache has been updated at that time. This event fires when the cache refreshers execute on a server and is used to execute a cache refresher on the local environment - like the content cache refresher ... which is done in an async/background thread.
It's also worth noting that the media cache is updated asynchronously as well.
There is no event that you can bind to to know when the content cache is actually updated but you could add your own background polling (or similar) in the event your are currently using and check if the cache has been updated, then stop the polling when you know the cache contains the updated information.
Though that implementation was created before Umbraco implemented an even more async approach to cache refreshing so I'm not positive this will work 100% of the time either but you can give that a try.
I use the same approach like articulate on a other (7.2.5) site and there it works fine. But this site is a 7.3.0 and if you use the same approach I can't get the url of a item. I get at null reference exception when I do item.Url
When we were running on the RC and beta versions we didn't have this issue by the way. It has been reported after we upgraded to 7.3.0 final release
I'd first disable SEO Checker and see if that changes what you seen in your logs, who knows what that is doing, I've seen some strange issues with that plugin.
You'll also need to provide some more information on exactly what you are doing, code snippets, specific implementation details, etc... The information you have provided is quite minimal.
Site
- nl
-- Courses
--- Course 1
--- Course 2
- en
- data
-- Exercises
--- Exercise 1
--- Exercise 2
We create exercises in the data folder. On each course we can select the exercises that make up the course. So exercises can be used in multiple courses across different languages.
A requirement that is each assigned exercise should have unique url based on the course it is assigned. We solve this using virtual routes.
This is the code of the virtual route handler :
public class ExerciseNodeHandler : UmbracoVirtualNodeByIdRouteHandler
{
private readonly int courseId;
/// <summary>
/// Initializes a new instance of the <see cref="CourseNodeHandler"/> class.
/// </summary>
/// <param name="realNodeId">
/// Real node id.
/// </param>
/// <param name="courseId">
/// Journey chapter id.
/// </param>
public CourseNodeHandler(int realNodeId, int courseId)
: base(realNodeId)
{
this.courseId = courseId;
}
/// <summary>
/// The find content.
/// </summary>
/// <param name="requestContext">
/// The request context.
/// </param>
/// <param name="umbracoContext">
/// The umbraco context.
/// </param>
/// <param name="baseContent">
/// The base content.
/// </param>
/// <returns>
/// The <see cref="IPublishedContent"/>.
/// </returns>
protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext, IPublishedContent baseContent)
{
// Store the id of the journey chapter in the RequestCache because we need it in other places.
var cacheManager = DependencyResolver.Current.GetService<ICacheManager>();
cacheManager.RequestCache.GetOrSet(Common.Constants.CacheKey.CourseIdForExercise, () => this.courseId);
return baseContent;
}
}
This is how we build the virtual routes
var exercises = this.FunctionThatGetsExercises()
foreach(var exercise in exercises)
{
RouteTable.Routes.MapUmbracoRoute(
string.Format("{0}-{1}-{2}", Routes.ExerciseRouteKey, exercise.CourseId, exercise.Id),
exercise.Url,
new
{
controller = "ExerciseController",
action = exerciseAction
},
new ExerciseNodeHandler(exercise.Id, exercise.CourseId));
}
We see that routes get build after (un)publish action. Also the node handler gets hit by when we request the page. But sometimes it returns a 404 which is gone after a while without any content actions.
What I think is strange in the log is the last line here.
Unfortunately I'm not really sure how much I can assist with this without debugging your own code. All I can do is really ask you more questions and provide some suggestions..
Disable SEO Checker
Your custom routes will be hit before any of the PublishedContentRequestEngine stuff kicks in, so for your custom routes you shouldn't see any of those logs. These are just MVC routes and Umbraco will ignore trying to route to those paths ... as long as your routes are created in the correct startup phase. Where/When are your routes created?
When you create your routes based on events, are you clearing the previously made routes?
Are you ensuring that you are locking the route table before modifying it?
When you are re-creating your routes based on events, these routes should be placed above Umbraco's catch all route in your route table.
I have been debugging this against the umbraco source.
I narrowed the issue down to this part of the code in UmbracoModule.ProcessRequest
// do not process if this request is not a front-end routable page
var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext);
//raise event here
OnRouteAttempt(new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext, httpContext));
if (!isRoutableAttempt.Success)
{
return;
}
When we see our 404 isRoutableAttempt returns succes. When we don't end up on 404 this is false.
I think it comes down to this method :
GlobalSettings.IsReservedPathOrUrl
public static bool IsReservedPathOrUrl(string url, HttpContextBase httpContext, RouteCollection routes)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
if (routes == null) throw new ArgumentNullException("routes");
//check if the current request matches a route, if so then it is reserved.
var route = routes.GetRouteData(httpContext);
if (route != null)
return true;
//continue with the standard ignore routine
return IsReservedPathOrUrl(url);
}
For some reason Umbraco doesn't see it as a reserved path yet when we have a 404.
It's because Umbraco checks the route table to see if there is a real route declared for a given URL. If there is no route found using routes.GetRouteData(httpContext) then it will not be detected as a reserved path and Umbraco will try to route the request ... which of course it cannot find.
The other problem is most likely because your re-created routes are placed after the Umbraco catch all route which should be last.
Yes, when you create your routes on startup, it will be before Umbraco adds the catch all route = ok. Then you use an event to delete your routes and re-add them... these get added to the end of the table, after the Umbraco catch all route. You'll want to insert them before that entry.
New virtual route shows 404 after creation
We are developing solution, that uses virtual routes with
UmbracoVirtualNodeByIdRouteHandler
for resolving.Some content is located in storage and can be reused across different pages. So we have node in system:
http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-2
User can assign content to this node. So virtual route can be something like
http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-2/vrt-1
Currently we have problem, that when editor creates new node, e.g.
journey-chapter-3
and then assignsvrt-1
to it, url http://dev.Demoportal.com/nl/journey-overview/journey-introduction-2/journey-detail-2/journey-chapter-3/vrt-1 returns 404. While debugging I see, that it's correctly resolved by our customUmbracoVirtualNodeByIdRouteHandler
But it doesn't go further to controller. Also strange thing found in logs, that it makes 2 requests to the page:Then after some time (no actions are taken) it starts working correctly and makes only 1 request to page:
Any ideas, why it's happening?
Sounds like you are creating a new route dynamically. You haven't explained that process at all.
Hi Shannon,
We are indeed creating the routes dynamicly.
We hook into the PageCacheRefresher.CacheUpdated event to clear and recreate our virtual routes.
Dave
The problem with that is that there is no guarantee that the cache has been updated at that time. This event fires when the cache refreshers execute on a server and is used to execute a cache refresher on the local environment - like the content cache refresher ... which is done in an async/background thread.
It's also worth noting that the media cache is updated asynchronously as well.
There is no event that you can bind to to know when the content cache is actually updated but you could add your own background polling (or similar) in the event your are currently using and check if the cache has been updated, then stop the polling when you know the cache contains the updated information.
Articulate does it slightly different and re-routes at the end of the request execution if the current request contains a token to check for route rebuilding: https://github.com/Shazwazza/Articulate/blob/d849d2872c6935f57e5177106f82ff3715fc0ace/src/Articulate/UmbracoEventHandler.cs#L80
Though that implementation was created before Umbraco implemented an even more async approach to cache refreshing so I'm not positive this will work 100% of the time either but you can give that a try.
Hi Shannon,
I use the same approach like articulate on a other (7.2.5) site and there it works fine. But this site is a 7.3.0 and if you use the same approach I can't get the url of a item. I get at null reference exception when I do item.Url
When we were running on the RC and beta versions we didn't have this issue by the way. It has been reported after we upgraded to 7.3.0 final release
Dave
I'd first disable SEO Checker and see if that changes what you seen in your logs, who knows what that is doing, I've seen some strange issues with that plugin.
You'll also need to provide some more information on exactly what you are doing, code snippets, specific implementation details, etc... The information you have provided is quite minimal.
Hi Shannon,
First let me explain how we setup the content
We have a structure like this
We create exercises in the data folder. On each course we can select the exercises that make up the course. So exercises can be used in multiple courses across different languages.
A requirement that is each assigned exercise should have unique url based on the course it is assigned. We solve this using virtual routes.
This is the code of the virtual route handler :
This is how we build the virtual routes
We see that routes get build after (un)publish action. Also the node handler gets hit by when we request the page. But sometimes it returns a 404 which is gone after a while without any content actions.
What I think is strange in the log is the last line here.
Here the first part of the url that is "nl" is suddenly changed to the node id of the nl node.
Dave
Unfortunately I'm not really sure how much I can assist with this without debugging your own code. All I can do is really ask you more questions and provide some suggestions..
Hi Shannon,
All help is appreciated.
Disabling SEO Checker doesn't have any affect
Routes are created in the ApplicationStarted event. When this happens all routes work as expected. The problems start when we are recreating them
We delete our routes on content change events inside a lock.
How do we do this ?
Dave
Hi Shannon,
I have been debugging this against the umbraco source.
I narrowed the issue down to this part of the code in UmbracoModule.ProcessRequest
When we see our 404 isRoutableAttempt returns succes. When we don't end up on 404 this is false.
I think it comes down to this method : GlobalSettings.IsReservedPathOrUrl
For some reason Umbraco doesn't see it as a reserved path yet when we have a 404.
Dave
It's because Umbraco checks the route table to see if there is a real route declared for a given URL. If there is no route found using
routes.GetRouteData(httpContext)
then it will not be detected as a reserved path and Umbraco will try to route the request ... which of course it cannot find.The other problem is most likely because your re-created routes are placed after the Umbraco catch all route which should be last.
MSDN has docs on using the RouteTable: https://msdn.microsoft.com/en-us/library/system.web.routing.routecollection(v=vs.110).aspx
The last route in our route table before we set our routes is one created by nuPickers.
Dave
Yes, when you create your routes on startup, it will be before Umbraco adds the catch all route = ok. Then you use an event to delete your routes and re-add them... these get added to the end of the table, after the Umbraco catch all route. You'll want to insert them before that entry.
Hi Shannon,
Changed my code to create the routes to this :
This seems to fix the problem. Let me update some code and deploy it to our load balanced environment and see if it get's fixed there as well.
Dave
Hi Shannon,
It seems to issue still exists even when I insert the routes in the beginning of the route table.
Debugger still shows that Umbraco doesn't see it as a reserved path and the normal pipeline is executed...which results in a 404
Dave
Probably gonna ditch the virtual routes and implement my own content finder. This is taking to long :-)
Dave
I'll try to replicate this, have added an issue: http://issues.umbraco.org/issue/U4-7230
is working on a reply...