Implements singly registered RoutesCache object and re-interfaces IRoutesCache which can now be

set at runtime. Exposes UmbracoContext via DocumentRequest which simplifies some things. Removes
dependency on UmbracoContext from DefaultRoutesCache, now InPreviewMode is checked before passing
processing to the IRoutesCache provider. Changed NiceUrls to NiceUrlResolver.
This commit is contained in:
shannon@ShandemVaio
2012-07-20 23:10:43 +06:00
parent 1b9e226f61
commit 6c872e8a65
13 changed files with 228 additions and 145 deletions

View File

@@ -12,8 +12,15 @@ namespace Umbraco.Web
/// </summary>
internal class ContentStore
{
private readonly UmbracoContext _umbracoContext;
/// <summary>
/// Delegate to return the current UmbracoContext
/// </summary>
private readonly UmbracoContext _umbracoContext;
/// <summary>
/// Constructor accepting a delegate to resolve the UmbracoContext
/// </summary>
/// <param name="umbracoContext"></param>
public ContentStore(UmbracoContext umbracoContext)
{
_umbracoContext = umbracoContext;

View File

@@ -7,9 +7,12 @@ using umbraco.cms.businesslogic.web;
namespace Umbraco.Web
{
internal class NiceUrls
/// <summary>
/// Resolves NiceUrls for a given node id
/// </summary>
internal class NiceUrlResolver
{
public NiceUrls(ContentStore contentStore, UmbracoContext umbracoContext, RoutesCache routesCache)
public NiceUrlResolver(ContentStore contentStore, UmbracoContext umbracoContext, IRoutesCache routesCache)
{
_umbracoContext = umbracoContext;
_contentStore = contentStore;
@@ -18,7 +21,7 @@ namespace Umbraco.Web
private readonly UmbracoContext _umbracoContext;
private readonly ContentStore _contentStore;
private readonly RoutesCache _routesCache;
private readonly IRoutesCache _routesCache;
// note: this could be a parameter...
const string UrlNameProperty = "@urlName";
@@ -34,10 +37,13 @@ namespace Umbraco.Web
public virtual string GetNiceUrl(int nodeId, int startNodeDepth, bool forceDomain)
{
string route;
string path;
string path;
// will not read cache if previewing!
var route = !_umbracoContext.InPreviewMode
? _routesCache.GetRoute(nodeId)
: null;
route = _routesCache.GetRoute(nodeId); // will not read cache if previewing
if (route != null)
{
int pos = route.IndexOf('/');
@@ -85,7 +91,11 @@ namespace Umbraco.Web
parts.Reverse();
path = "/" + string.Join("/", parts);
route = string.Format("{0}{1}", id, path);
_routesCache.Store(nodeId, route); // will not write if previewing
if (!_umbracoContext.InPreviewMode)
{
_routesCache.Store(nodeId, route); // will not write if previewing
}
return FormatUrl(domain, path);
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Web.Routing
{
/// <summary>
/// The default implementation of IRoutesCache
/// </summary>
internal class DefaultRoutesCache : IRoutesCache
{
private readonly object _lock = new object();
private Dictionary<int, string> _routes;
private Dictionary<string, int> _nodeIds;
public DefaultRoutesCache()
{
Clear();
// here we should register handlers to clear the cache when content changes
// at the moment this is done by library, which clears everything when content changed
//
// but really, we should do some partial refreshes!
// otherwise, we could even cache 404 errors...
}
public void Store(int nodeId, string route)
{
lock (_lock)
{
_routes[nodeId] = route;
_nodeIds[route] = nodeId;
}
}
public string GetRoute(int nodeId)
{
lock (_lock)
{
return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null;
}
}
public int GetNodeId(string route)
{
lock (_lock)
{
return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0;
}
}
public void ClearNode(int nodeId)
{
lock (_lock)
{
if (_routes.ContainsKey(nodeId))
{
_nodeIds.Remove(_routes[nodeId]);
_routes.Remove(nodeId);
}
}
}
public void Clear()
{
lock (_lock)
{
_routes = new Dictionary<int, string>();
_nodeIds = new Dictionary<string, int>();
}
}
}
}

View File

@@ -16,12 +16,12 @@ namespace Umbraco.Web.Routing
{
static readonly TraceSource Trace = new TraceSource("DocumentRequest");
public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrls niceUrls)
public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrlResolver niceUrlResolver)
{
// register lookups
_environment = lookups;
_umbracoContext = umbracoContext;
_niceUrls = niceUrls;
UmbracoContext = umbracoContext;
_niceUrlResolver = niceUrlResolver;
// prepare the host
var host = uri.Host;
@@ -55,13 +55,25 @@ namespace Umbraco.Web.Routing
this.QueryString = uri.Query.TrimStart('?');
}
readonly RoutingEnvironment _environment;
private readonly NiceUrlResolver _niceUrlResolver;
/// <summary>
/// the id of the requested node, if any, else zero.
/// </summary>
int _nodeId = 0;
/// <summary>
/// the requested node, if any, else null.
/// </summary>
XmlNode _node = null;
#region Properties
// the id of the requested node, if any, else zero.
int _nodeId = 0;
// the requested node, if any, else null.
XmlNode _node = null;
/// <summary>
/// Returns the current UmbracoContext
/// </summary>
public UmbracoContext UmbracoContext { get; private set; }
/// <summary>
/// Gets the request host name.
@@ -176,15 +188,9 @@ namespace Umbraco.Web.Routing
#region Resolve
readonly RoutingEnvironment _environment;
private readonly UmbracoContext _umbracoContext;
private readonly NiceUrls _niceUrls;
/// <summary>
/// Determines the site root (if any) matching the http request.
/// </summary>
/// <param name="host">The host name part of the http request, eg. <c>www.example.com</c>.</param>
/// <param name="url">The url part of the http request, starting with a slash, eg. <c>/foo/bar</c>.</param>
/// </summary>
/// <returns>A value indicating whether a domain was found.</returns>
public bool ResolveSiteRoot()
{
@@ -432,9 +438,9 @@ namespace Umbraco.Web.Routing
if (this.Node == null)
throw new InvalidOperationException("There is no node.");
var templateAlias = _umbracoContext.HttpContext.Request.QueryString["altTemplate"];
var templateAlias = UmbracoContext.HttpContext.Request.QueryString["altTemplate"];
if (string.IsNullOrWhiteSpace(templateAlias))
templateAlias = _umbracoContext.HttpContext.Request.Form["altTemplate"];
templateAlias = UmbracoContext.HttpContext.Request.Form["altTemplate"];
// fixme - we might want to support cookies?!? NO but provide a hook to change the template
@@ -488,7 +494,7 @@ namespace Umbraco.Web.Routing
redirectId = -1;
string redirectUrl = "#";
if (redirectId > 0)
redirectUrl = _niceUrls.GetNiceUrl(redirectId);
redirectUrl = _niceUrlResolver.GetNiceUrl(redirectId);
if (redirectUrl != "#")
this.RedirectUrl = redirectUrl;
}

View File

@@ -0,0 +1,11 @@
namespace Umbraco.Web.Routing
{
internal interface IRoutesCache
{
void Store(int nodeId, string route);
string GetRoute(int nodeId);
int GetNodeId(string route);
void ClearNode(int nodeId);
void Clear();
}
}

View File

@@ -9,7 +9,7 @@ namespace Umbraco.Web.Routing
[LookupWeight(10)]
internal class LookupByPath : ILookup
{
public LookupByPath(ContentStore contentStore, RoutesCache routesCache)
public LookupByPath(ContentStore contentStore, IRoutesCache routesCache)
{
ContentStore = contentStore;
RoutesCache = routesCache;
@@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing
static readonly TraceSource Trace = new TraceSource("LookupByPath");
protected ContentStore ContentStore;
protected RoutesCache RoutesCache;
protected IRoutesCache RoutesCache;
public virtual bool LookupDocument(DocumentRequest docreq)
{
@@ -31,7 +31,12 @@ namespace Umbraco.Web.Routing
{
Trace.TraceInformation("Test route \"{0}\"", route);
var nodeId = RoutesCache.GetNodeId(route);
//return '0' if in preview mode!
var nodeId = !docreq.UmbracoContext.InPreviewMode
? RoutesCache.GetNodeId(route)
: 0;
XmlNode node = null;
if (nodeId > 0)
{
@@ -55,7 +60,12 @@ namespace Umbraco.Web.Routing
{
docreq.Node = node;
Trace.TraceInformation("Query matches, id={0}", docreq.NodeId);
RoutesCache.Store(docreq.NodeId, route);
if (!docreq.UmbracoContext.InPreviewMode)
{
RoutesCache.Store(docreq.NodeId, route); // will not write if previewing
}
}
else
{

View File

@@ -13,7 +13,7 @@ namespace Umbraco.Web.Routing
{
static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate");
public LookupByPathWithTemplate(ContentStore contentStore, RoutesCache routesCache)
public LookupByPathWithTemplate(ContentStore contentStore, IRoutesCache routesCache)
: base(contentStore, routesCache)
{
}

View File

@@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing
static readonly TraceSource Trace = new TraceSource("LookupByProfile");
public LookupByProfile(ContentStore contentStore, RoutesCache routesCache, UmbracoContext umbracoContext)
public LookupByProfile(ContentStore contentStore, IRoutesCache routesCache, UmbracoContext umbracoContext)
: base(contentStore, routesCache)
{
_umbracoContext = umbracoContext;

View File

@@ -1,81 +1,57 @@
using System.Collections.Generic;
using System;
namespace Umbraco.Web.Routing
{
internal class RoutesCache
{
private readonly object _lock = new object();
private Dictionary<int, string> _routes;
private Dictionary<string, int> _nodeIds;
/// <summary>
/// A singly registered object to assign and get the current IRoutesCache provider
/// </summary>
internal class RoutesCache
{
private static readonly RoutesCache Instance = new RoutesCache();
private static IRoutesCache _provider;
private readonly UmbracoContext _umbracoContext;
/// <summary>
/// public contructor assigns the DefaultRoutesCache as the default provider
/// </summary>
public RoutesCache()
:this(null)
{
}
public RoutesCache(UmbracoContext umbracoContext)
{
_umbracoContext = umbracoContext;
/// <summary>
/// Constructor sets a custom provider if specified
/// </summary>
/// <param name="provider"></param>
internal RoutesCache(IRoutesCache provider)
{
_provider = provider ?? new DefaultRoutesCache();
}
Clear();
/// <summary>
/// Set the routes cache provider
/// </summary>
/// <param name="provider"></param>
public static void SetProvider(IRoutesCache provider)
{
if (provider == null) throw new ArgumentNullException("provider");
_provider = provider;
}
// here we should register handlers to clear the cache when content changes
// at the moment this is done by library, which clears everything when content changed
//
// but really, we should do some partial refreshes!
// otherwise, we could even cache 404 errors...
}
/// <summary>
/// Singleton accessor
/// </summary>
public static RoutesCache Current
{
get { return Instance; }
}
public void Store(int nodeId, string route)
{
if (_umbracoContext.InPreviewMode)
return;
lock (_lock)
{
_routes[nodeId] = route;
_nodeIds[route] = nodeId;
}
}
public string GetRoute(int nodeId)
{
if (_umbracoContext.InPreviewMode)
return null;
lock (_lock)
{
return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null;
}
}
public int GetNodeId(string route)
{
if (_umbracoContext.InPreviewMode)
return 0;
lock (_lock)
{
return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0;
}
}
public void ClearNode(int nodeId)
{
lock (_lock)
{
if (_routes.ContainsKey(nodeId))
{
_nodeIds.Remove(_routes[nodeId]);
_routes.Remove(nodeId);
}
}
}
public void Clear()
{
lock (_lock)
{
_routes = new Dictionary<int, string>();
_nodeIds = new Dictionary<string, int>();
}
}
}
/// <summary>
/// Get the current provider
/// </summary>
/// <returns></returns>
public IRoutesCache GetProvider()
{
return _provider;
}
}
}

View File

@@ -3,10 +3,11 @@ using System.Linq;
namespace Umbraco.Web.Routing
{
// represents a request for one specified Umbraco document to be rendered
// by one specified template, using one particular culture.
//
/// <summary>
/// represents a request for one specified Umbraco document to be rendered by one specified template,
/// using one particular culture.
/// </summary>
internal class RoutingEnvironment
{
public RoutingEnvironment(
@@ -28,13 +29,9 @@ namespace Umbraco.Web.Routing
}).ToList();
}
public IEnumerable<ILookup> Lookups
{
get;
private set;
}
public IEnumerable<ILookup> Lookups { get; private set; }
public ILookupNotFound LookupNotFound { get; private set; }
public ILookupNotFound LookupNotFound { get; private set; }
public ContentStore ContentStore { get; private set; }

View File

@@ -240,11 +240,12 @@
<Compile Include="ContentStore.cs" />
<Compile Include="LegacyRequestInitializer.cs" />
<Compile Include="Mvc\ControllerExtensions.cs" />
<Compile Include="NiceUrls.cs" />
<Compile Include="NiceUrlResolver.cs" />
<Compile Include="PluginResolverExtensions.cs" />
<Compile Include="Routing\DocumentRequest.cs" />
<Compile Include="Routing\ILookup.cs" />
<Compile Include="Routing\ILookupNotFound.cs" />
<Compile Include="Routing\IRoutesCache.cs" />
<Compile Include="Routing\LookupByAlias.cs" />
<Compile Include="Routing\LookupById.cs" />
<Compile Include="Routing\LookupByPath.cs" />
@@ -252,6 +253,7 @@
<Compile Include="Routing\LookupByProfile.cs" />
<Compile Include="Routing\LookupFor404.cs" />
<Compile Include="Routing\LookupWeightAttribute.cs" />
<Compile Include="Routing\DefaultRoutesCache.cs" />
<Compile Include="Routing\RoutesCache.cs" />
<Compile Include="Routing\RoutingEnvironment.cs" />
<Compile Include="umbraco.presentation\EnsureSystemPathsApplicationStartupHandler.cs" />

View File

@@ -49,7 +49,7 @@ namespace Umbraco.Web
/// <summary>
/// Gets the current Umbraco Context.
/// </summary>
public static UmbracoContext Current
public static UmbracoContext Current
{
get
{
@@ -126,6 +126,16 @@ namespace Umbraco.Web
/// </summary>
internal DocumentRequest DocumentRequest { get; set; }
/// <summary>
/// Gets/sets the NiceUrlResolver object
/// </summary>
internal NiceUrlResolver NiceUrlResolver { get; set; }
/// <summary>
/// Gets/sets the RoutingEnvironment object
/// </summary>
internal RoutingEnvironment RoutingEnvironment { get; set; }
/// <summary>
/// Exposes the HttpContext for the current request
/// </summary>
@@ -193,29 +203,7 @@ namespace Umbraco.Web
&& !currentUrl.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)); // is not in admin UI
}
}
/// <summary>
/// Gets the current Live Editing Context.
/// </summary>
public virtual ILiveEditingContext LiveEditingContext
{
get
{
//TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61
var value = (ILiveEditingContext)HttpContext.Items["LiveEditingContext"];
if (value == null)
{
LiveEditingContext = value = new DefaultLiveEditingContext();
}
return value;
}
set
{
//TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61
HttpContext.Items["LiveEditingContext"] = value;
}
}
}
}

View File

@@ -37,11 +37,9 @@ namespace Umbraco.Web
UmbracoContext.Current = umbracoContext;
//create a content store
var contentStore = new ContentStore(umbracoContext);
//create the routes cache
var routesCache = new RoutesCache(umbracoContext);
var contentStore = new ContentStore(umbracoContext);
//create the nice urls
var niceUrls = new NiceUrls(contentStore, umbracoContext, routesCache);
var niceUrls = new NiceUrlResolver(contentStore, umbracoContext, RoutesCache.Current.GetProvider());
//create the RoutingEnvironment (one per http request as it relies on the umbraco context!)
var routingEnvironment = new RoutingEnvironment(
ApplicationContext.Current.Plugins.ResolveLookups().ToArray(),
@@ -49,9 +47,15 @@ namespace Umbraco.Web
contentStore);
// create the new document request which will cleanup the uri once and for all
var docreq = new DocumentRequest(uri, routingEnvironment, umbracoContext, niceUrls);
// initialize the document request on the UmbracoContext (this is circular dependency!!!)
//NOTE: we are putting these objects on the UmbracoContext because these might be handy for developers in the future to
// access if we make them public.
// initialize the DocumentRequest on the UmbracoContext (this is circular dependency!!!)
umbracoContext.DocumentRequest = docreq;
// initialize the RoutingEnvironment on the UmbracoContext (this is circular dependency!!!)
umbracoContext.RoutingEnvironment = routingEnvironment;
// initialize the RoutingEnvironment on the UmbracoContext (this is circular dependency!!!)
umbracoContext.NiceUrlResolver = niceUrls;
//create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!)
var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext);