U4-2549 - fix issue with last chance content finder

Conflicts:
	src/Umbraco.Web/Umbraco.Web.csproj

Conflicts:
	src/Umbraco.Web/Umbraco.Web.csproj
This commit is contained in:
Stephan
2013-08-26 15:47:48 +02:00
committed by Sebastiaan Janssen
parent fbfdd8d398
commit 9e2733ce69
8 changed files with 279 additions and 119 deletions

View File

@@ -3,5 +3,5 @@
<notFound assembly="umbraco" type="SearchForAlias" />
<notFound assembly="umbraco" type="SearchForTemplate"/>
<notFound assembly="umbraco" type="SearchForProfile"/>
<notFound assembly="umbraco" type="handle404"/>
<notFound assembly="umbraco" type="handle404" last="true"/>
</NotFoundHandlers>

View File

@@ -3,5 +3,5 @@
<notFound assembly="umbraco" type="SearchForAlias" />
<notFound assembly="umbraco" type="SearchForTemplate"/>
<notFound assembly="umbraco" type="SearchForProfile"/>
<notFound assembly="umbraco" type="handle404"/>
<notFound assembly="umbraco" type="handle404" last="true"/>
</NotFoundHandlers>

View File

@@ -88,14 +88,25 @@ namespace Umbraco.Web.Routing
#region Utilities
private bool FindByUrlAliasEnabled
private static bool FindByUrlAliasEnabled
{
get
{
var hasFinder = ContentFinderResolver.Current.ContainsType<ContentFinderByUrlAlias>();
var hasHandler = ContentFinderResolver.Current.ContainsType<ContentFinderByNotFoundHandlers>()
&& NotFoundHandlerHelper.CustomHandlerTypes.Contains(typeof(global::umbraco.SearchForAlias));
return hasFinder || hasHandler;
// finder
if (ContentFinderResolver.Current.ContainsType<ContentFinderByUrlAlias>())
return true;
// handler wrapped into a finder
if (ContentFinderResolver.Current.ContainsType<ContentFinderByNotFoundHandler<global::umbraco.SearchForAlias>>())
return true;
// handler wrapped into special finder
if (ContentFinderResolver.Current.ContainsType<ContentFinderByNotFoundHandlers>()
&& NotFoundHandlerHelper.IsNotFoundHandlerEnabled<global::umbraco.SearchForAlias>())
return true;
// anything else, we can't detect
return false;
}
}

View File

@@ -29,115 +29,77 @@ namespace Umbraco.Web.Routing
#region Copied over and adapted from presentation.requestHandler
void HandlePageNotFound(PublishedContentRequest docRequest)
private static void HandlePageNotFound(PublishedContentRequest docRequest)
{
var url = NotFoundHandlerHelper.GetLegacyUrlForNotFoundHandlers();
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Running for legacy url='{0}'.", () => url);
foreach (var handler in GetNotFoundHandlers())
foreach (var handler in NotFoundHandlerHelper.GetNotFoundHandlers())
{
IContentFinder finder = null;
var handlerName = handler.GetType().FullName;
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}'.", () => handlerName);
// replace with our own implementation
if (handler is global::umbraco.SearchForAlias)
finder = new ContentFinderByUrlAlias();
else if (handler is global::umbraco.SearchForProfile)
finder = new ContentFinderByProfile();
else if (handler is global::umbraco.SearchForTemplate)
finder = new ContentFinderByNiceUrlAndTemplate();
else if (handler is global::umbraco.handle404)
finder = new ContentFinderByLegacy404();
var finder = NotFoundHandlerHelper.SubsituteFinder(handler);
if (finder != null)
{
var finderName = finder.GetType().FullName;
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Replace handler '{0}' by new finder '{1}'.", () => handlerName, () => finderName);
if (finder.TryFindContent(docRequest))
{
// do NOT set docRequest.PublishedContent again here as
// it would clear any template that the finder might have set
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
if (docRequest.Is404)
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' set status to 404.", () => finderName);
// if we found a document, break, don't look at more handler -- we're done
break;
}
// can't find a document => continue with other handlers
if (finder.TryFindContent(docRequest) == false)
continue;
// if we did not find a document, continue, look at other handlers
continue;
}
// found a document => break, don't run other handlers, we're done
// else it's a legacy handler, run
// in theory an IContentFinder can return true yet set no document
// but none of the substitued finders (see SubstituteFinder) do it.
if (handler.Execute(url) && handler.redirectID > 0)
{
var redirectId = handler.redirectID;
docRequest.PublishedContent = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(redirectId);
// do NOT set docRequest.PublishedContent again here
// as it would clear any template that the finder might have set
if (!docRequest.HasPublishedContent)
{
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
break;
}
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
if (docRequest.Is404)
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Finder '{0}' set status to 404.", () => finderName);
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
{
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' set status code to 404.", () => handlerName);
docRequest.Is404 = true;
}
//// check for caching
//if (handler.CacheUrl)
//{
// if (url.StartsWith("/"))
// url = "/" + url;
// var cacheKey = (currentDomain == null ? "" : currentDomain.Name) + url;
// var culture = currentDomain == null ? null : currentDomain.Language.CultureAlias;
// SetCache(cacheKey, new CacheEntry(handler.redirectID.ToString(), culture));
// HttpContext.Current.Trace.Write("NotFoundHandler",
// string.Format("Added to cache '{0}', {1}.", url, handler.redirectID));
//}
// if we found a document, break, don't look at more handler -- we're done
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handlerName, () => docRequest.PublishedContent.Id);
break;
}
// if we did not find a document, continue, look at other handlers
// else it's a legacy handler: run
// can't find a document => continue with other handlers
if (handler.Execute(url) == false || handler.redirectID <= 0)
continue;
// found a document ID => ensure it's a valid document
var redirectId = handler.redirectID;
docRequest.PublishedContent = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(redirectId);
if (docRequest.HasPublishedContent == false)
{
// the handler said it could handle the url, and returned a content ID
// yet that content ID is invalid... should we run the other handlers?
// I don't think so, not here, let the "last chance" finder take care.
// so, break.
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
break;
}
// found a valid document => break, don't run other handlers, we're done
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
{
LogHelper.Debug<ContentFinderByNotFoundHandlers>("Handler '{0}' set status code to 404.", () => handlerName);
docRequest.Is404 = true;
}
break;
}
}
IEnumerable<INotFoundHandler> GetNotFoundHandlers()
{
// instanciate new handlers
// using definition cache
var handlers = new List<INotFoundHandler>();
foreach (var type in NotFoundHandlerHelper.CustomHandlerTypes)
{
try
{
var handler = Activator.CreateInstance(type) as INotFoundHandler;
if (handler != null)
handlers.Add(handler);
}
catch (Exception e)
{
LogHelper.Error<ContentFinderByNotFoundHandlers>(string.Format("Error instanciating handler {0}, ignoring.", type.FullName), e);
}
}
return handlers;
}
#endregion
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using umbraco.interfaces;
namespace Umbraco.Web.Routing
{
/// <summary>
/// Provides an implementation of <see cref="IContentFinder"/> that runs legacy <c>INotFoundHandler</c> in "last chance" situation.
/// </summary>
public class ContentLastChanceFinderByNotFoundHandlers : IContentFinder
{
// notes
//
// at the moment we load the legacy INotFoundHandler
// excluding those that have been replaced by proper finders,
// and run them.
/// <summary>
/// Tries to find and assign an Umbraco document to a <c>PublishedContentRequest</c>.
/// </summary>
/// <param name="docRequest">The <c>PublishedContentRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
public bool TryFindContent(PublishedContentRequest docRequest)
{
HandlePageNotFound(docRequest);
return docRequest.HasPublishedContent;
}
#region Copied over and adapted from presentation.requestHandler
private static void HandlePageNotFound(PublishedContentRequest docRequest)
{
var url = NotFoundHandlerHelper.GetLegacyUrlForNotFoundHandlers();
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Running for legacy url='{0}'.", () => url);
var handler = NotFoundHandlerHelper.GetNotFoundLastChanceHandler();
var handlerName = handler.GetType().FullName;
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Handler '{0}'.", () => handlerName);
var finder = NotFoundHandlerHelper.SubsituteFinder(handler);
if (finder != null)
{
var finderName = finder.GetType().FullName;
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Replace handler '{0}' by new finder '{1}'.", () => handlerName, () => finderName);
// can't find a document => exit
if (finder.TryFindContent(docRequest) == false)
return;
// found a document => we're done
// in theory an IContentFinder can return true yet set no document
// but none of the substitued finders (see SubstituteFinder) do it.
// do NOT set docRequest.PublishedContent again here
// as it would clear any template that the finder might have set
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
if (docRequest.Is404)
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Finder '{0}' set status to 404.", () => finderName);
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handlerName, () => docRequest.PublishedContent.Id);
return;
}
// else it's a legacy handler, run
// can't find a document => exit
if (handler.Execute(url) == false || handler.redirectID <= 0)
return;
// found a document ID => ensure it's a valid document
var redirectId = handler.redirectID;
docRequest.PublishedContent = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(redirectId);
if (docRequest.HasPublishedContent == false)
{
// the handler said it could handle the url, and returned a content ID
// yet that content ID is invalid... exit.
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
return;
}
// found a valid document => return
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
{
LogHelper.Debug<ContentLastChanceFinderByNotFoundHandlers>("Handler '{0}' set status code to 404.", () => handlerName);
docRequest.Is404 = true;
}
}
#endregion
}
}

View File

@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Reflection;
using Umbraco.Core;
using Umbraco.Core.Logging;
using umbraco.interfaces;
namespace Umbraco.Web.Routing
{
@@ -36,14 +39,14 @@ namespace Umbraco.Web.Routing
return url;
// code from requestModule.UmbracoRewrite
string tmp = httpContext.Request.Path.ToLower();
var tmp = httpContext.Request.Path.ToLower();
// note: requestModule.UmbracoRewrite also did some stripping of &umbPage
// from the querystring... that was in v3.x to fix some issues with pre-forms
// auth. Paul Sterling confirmed in jan. 2013 that we can get rid of it.
// code from requestHandler.cleanUrl
string root = Core.IO.SystemDirectories.Root.ToLower();
var root = Core.IO.SystemDirectories.Root.ToLower();
if (!string.IsNullOrEmpty(root) && tmp.StartsWith(root))
tmp = tmp.Substring(root.Length);
tmp = tmp.TrimEnd('/');
@@ -55,7 +58,7 @@ namespace Umbraco.Web.Routing
// code from UmbracoDefault.Page_PreInit
if (tmp != "" && httpContext.Request["umbPageID"] == null)
{
string tryIntParse = tmp.Replace("/", "").Replace(".aspx", string.Empty);
var tryIntParse = tmp.Replace("/", "").Replace(".aspx", string.Empty);
int result;
if (int.TryParse(tryIntParse, out result))
tmp = tmp.Replace(".aspx", string.Empty);
@@ -77,7 +80,8 @@ namespace Umbraco.Web.Routing
return tmp;
}
static IEnumerable<Type> _customHandlerTypes;
private static IEnumerable<Type> _customHandlerTypes;
private static Type _customLastChanceHandlerType;
static void InitializeNotFoundHandlers()
{
@@ -87,6 +91,8 @@ namespace Umbraco.Web.Routing
LogHelper.Debug<NotFoundHandlerHelper>("Registering custom handlers.");
var customHandlerTypes = new List<Type>();
Type customLastChanceHandlerType = null;
var hasLast = false;
var customHandlers = new XmlDocument();
customHandlers.Load(Core.IO.IOHelper.MapPath(Core.IO.SystemFiles.NotFoundhandlersConfig));
@@ -96,12 +102,23 @@ namespace Umbraco.Web.Routing
var assemblyName = n.Attributes.GetNamedItem("assembly").Value;
var typeName = n.Attributes.GetNamedItem("type").Value;
string ns = assemblyName;
var ns = assemblyName;
var nsAttr = n.Attributes.GetNamedItem("namespace");
if (nsAttr != null && !string.IsNullOrWhiteSpace(nsAttr.Value))
if (nsAttr != null && string.IsNullOrWhiteSpace(nsAttr.Value) == false)
ns = nsAttr.Value;
LogHelper.Debug<NotFoundHandlerHelper>("Registering '{0}.{1},{2}'.", () => ns, () => typeName, () => assemblyName);
var lcAttr = n.Attributes.GetNamedItem("last");
var last = lcAttr != null && lcAttr.Value != null && lcAttr.Value.InvariantEquals("true");
if (last) // there can only be one last chance handler in the config file
{
if (hasLast)
throw new Exception();
hasLast = true;
}
LogHelper.Debug<NotFoundHandlerHelper>("Registering '{0}.{1},{2}'{3}", () => ns, () => typeName, () => assemblyName,
() => last ? " (last)." : ".");
Type type = null;
try
@@ -114,19 +131,79 @@ namespace Umbraco.Web.Routing
LogHelper.Error<NotFoundHandlerHelper>("Error registering handler, ignoring.", e);
}
if (type != null)
if (type == null) continue;
if (last)
_customLastChanceHandlerType = type;
else
customHandlerTypes.Add(type);
}
_customHandlerTypes = customHandlerTypes;
_customHandlerTypes = customHandlerTypes.ToArray();
}
public static IEnumerable<Type> CustomHandlerTypes
public static IEnumerable<INotFoundHandler> GetNotFoundHandlers()
{
get
// instanciate new handlers
// using definition cache
var handlers = new List<INotFoundHandler>();
foreach (var type in _customHandlerTypes)
{
return _customHandlerTypes;
try
{
var handler = Activator.CreateInstance(type) as INotFoundHandler;
if (handler != null)
handlers.Add(handler);
}
catch (Exception e)
{
LogHelper.Error<ContentFinderByNotFoundHandlers>(string.Format("Error instanciating handler {0}, ignoring.", type.FullName), e);
}
}
return handlers;
}
public static bool IsNotFoundHandlerEnabled<T>()
{
return _customHandlerTypes.Contains(typeof (T));
}
public static INotFoundHandler GetNotFoundLastChanceHandler()
{
if (_customLastChanceHandlerType == null) return null;
try
{
var handler = Activator.CreateInstance(_customLastChanceHandlerType) as INotFoundHandler;
if (handler != null)
return handler;
}
catch (Exception e)
{
LogHelper.Error<ContentFinderByNotFoundHandlers>(string.Format("Error instanciating handler {0}, ignoring.", _customLastChanceHandlerType.FullName), e);
}
return null;
}
public static IContentFinder SubsituteFinder(INotFoundHandler handler)
{
IContentFinder finder = null;
if (handler is global::umbraco.SearchForAlias)
finder = new ContentFinderByUrlAlias();
else if (handler is global::umbraco.SearchForProfile)
finder = new ContentFinderByProfile();
else if (handler is global::umbraco.SearchForTemplate)
finder = new ContentFinderByNiceUrlAndTemplate();
else if (handler is global::umbraco.handle404)
finder = new ContentFinderByLegacy404();
return finder;
}
}
}

View File

@@ -292,6 +292,7 @@
<Compile Include="Configuration\WebRouting.cs" />
<Compile Include="Editors\MediaController.cs" />
<Compile Include="Models\ContentEditing\ContentSortOrder.cs" />
<Compile Include="Routing\ContentLastChanceFinderByNotFoundHandlers.cs" />
<Compile Include="Security\Providers\MembersMembershipProvider.cs" />
<Compile Include="Security\Providers\UsersMembershipProvider.cs" />
<Compile Include="Standalone\ServiceContextManager.cs" />

View File

@@ -302,23 +302,31 @@ namespace Umbraco.Web
typeof(DefaultUrlProvider)
);
// the legacy 404 will run from within ContentFinderByNotFoundHandlers below
// so for the time being there is no last chance finder
ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver();
ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver(
// handled by ContentLastChanceFinderByNotFoundHandlers for the time being
// soon as we get rid of INotFoundHandler support, we must enable this
//new ContentFinderByLegacy404()
// implement INotFoundHandler support... remove once we get rid of it
new ContentLastChanceFinderByNotFoundHandlers());
ContentFinderResolver.Current = new ContentFinderResolver(
// add all known resolvers in the correct order, devs can then modify this list
// on application startup either by binding to events or in their own global.asax
typeof (ContentFinderByPageIdQuery),
typeof (ContentFinderByNiceUrl),
typeof (ContentFinderByIdPath),
// these will be handled by ContentFinderByNotFoundHandlers
// so they can be enabled/disabled even though resolvers are not public yet
//typeof (ContentFinderByNiceUrlAndTemplate),
//typeof (ContentFinderByProfile),
//typeof (ContentFinderByUrlAlias),
typeof (ContentFinderByNotFoundHandlers)
);
// all built-in finders in the correct order, devs can then modify this list
// on application startup via an application event handler.
typeof (ContentFinderByPageIdQuery),
typeof (ContentFinderByNiceUrl),
typeof (ContentFinderByIdPath),
// these will be handled by ContentFinderByNotFoundHandlers so they can be enabled/disabled
// via the config file... soon as we get rid of INotFoundHandler support, we must enable
// them here.
//typeof (ContentFinderByNiceUrlAndTemplate),
//typeof (ContentFinderByProfile),
//typeof (ContentFinderByUrlAlias),
// implement INotFoundHandler support... remove once we get rid of it
typeof (ContentFinderByNotFoundHandlers)
);
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());