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> /// </summary>
internal class ContentStore 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) public ContentStore(UmbracoContext umbracoContext)
{ {
_umbracoContext = umbracoContext; _umbracoContext = umbracoContext;

View File

@@ -7,9 +7,12 @@ using umbraco.cms.businesslogic.web;
namespace Umbraco.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; _umbracoContext = umbracoContext;
_contentStore = contentStore; _contentStore = contentStore;
@@ -18,7 +21,7 @@ namespace Umbraco.Web
private readonly UmbracoContext _umbracoContext; private readonly UmbracoContext _umbracoContext;
private readonly ContentStore _contentStore; private readonly ContentStore _contentStore;
private readonly RoutesCache _routesCache; private readonly IRoutesCache _routesCache;
// note: this could be a parameter... // note: this could be a parameter...
const string UrlNameProperty = "@urlName"; const string UrlNameProperty = "@urlName";
@@ -34,10 +37,13 @@ namespace Umbraco.Web
public virtual string GetNiceUrl(int nodeId, int startNodeDepth, bool forceDomain) 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) if (route != null)
{ {
int pos = route.IndexOf('/'); int pos = route.IndexOf('/');
@@ -85,7 +91,11 @@ namespace Umbraco.Web
parts.Reverse(); parts.Reverse();
path = "/" + string.Join("/", parts); path = "/" + string.Join("/", parts);
route = string.Format("{0}{1}", id, path); 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); 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"); 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 // register lookups
_environment = lookups; _environment = lookups;
_umbracoContext = umbracoContext; UmbracoContext = umbracoContext;
_niceUrls = niceUrls; _niceUrlResolver = niceUrlResolver;
// prepare the host // prepare the host
var host = uri.Host; var host = uri.Host;
@@ -55,13 +55,25 @@ namespace Umbraco.Web.Routing
this.QueryString = uri.Query.TrimStart('?'); 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 #region Properties
// the id of the requested node, if any, else zero. /// <summary>
int _nodeId = 0; /// Returns the current UmbracoContext
/// </summary>
// the requested node, if any, else null. public UmbracoContext UmbracoContext { get; private set; }
XmlNode _node = null;
/// <summary> /// <summary>
/// Gets the request host name. /// Gets the request host name.
@@ -176,15 +188,9 @@ namespace Umbraco.Web.Routing
#region Resolve #region Resolve
readonly RoutingEnvironment _environment;
private readonly UmbracoContext _umbracoContext;
private readonly NiceUrls _niceUrls;
/// <summary> /// <summary>
/// Determines the site root (if any) matching the http request. /// Determines the site root (if any) matching the http request.
/// </summary> /// </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>
/// <returns>A value indicating whether a domain was found.</returns> /// <returns>A value indicating whether a domain was found.</returns>
public bool ResolveSiteRoot() public bool ResolveSiteRoot()
{ {
@@ -432,9 +438,9 @@ namespace Umbraco.Web.Routing
if (this.Node == null) if (this.Node == null)
throw new InvalidOperationException("There is no node."); 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)) 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 // 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; redirectId = -1;
string redirectUrl = "#"; string redirectUrl = "#";
if (redirectId > 0) if (redirectId > 0)
redirectUrl = _niceUrls.GetNiceUrl(redirectId); redirectUrl = _niceUrlResolver.GetNiceUrl(redirectId);
if (redirectUrl != "#") if (redirectUrl != "#")
this.RedirectUrl = 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)] [LookupWeight(10)]
internal class LookupByPath : ILookup internal class LookupByPath : ILookup
{ {
public LookupByPath(ContentStore contentStore, RoutesCache routesCache) public LookupByPath(ContentStore contentStore, IRoutesCache routesCache)
{ {
ContentStore = contentStore; ContentStore = contentStore;
RoutesCache = routesCache; RoutesCache = routesCache;
@@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing
static readonly TraceSource Trace = new TraceSource("LookupByPath"); static readonly TraceSource Trace = new TraceSource("LookupByPath");
protected ContentStore ContentStore; protected ContentStore ContentStore;
protected RoutesCache RoutesCache; protected IRoutesCache RoutesCache;
public virtual bool LookupDocument(DocumentRequest docreq) public virtual bool LookupDocument(DocumentRequest docreq)
{ {
@@ -31,7 +31,12 @@ namespace Umbraco.Web.Routing
{ {
Trace.TraceInformation("Test route \"{0}\"", route); 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; XmlNode node = null;
if (nodeId > 0) if (nodeId > 0)
{ {
@@ -55,7 +60,12 @@ namespace Umbraco.Web.Routing
{ {
docreq.Node = node; docreq.Node = node;
Trace.TraceInformation("Query matches, id={0}", docreq.NodeId); 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 else
{ {

View File

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

View File

@@ -18,7 +18,7 @@ namespace Umbraco.Web.Routing
static readonly TraceSource Trace = new TraceSource("LookupByProfile"); 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) : base(contentStore, routesCache)
{ {
_umbracoContext = umbracoContext; _umbracoContext = umbracoContext;

View File

@@ -1,81 +1,57 @@
using System.Collections.Generic; using System;
namespace Umbraco.Web.Routing namespace Umbraco.Web.Routing
{ {
internal class RoutesCache /// <summary>
{ /// A singly registered object to assign and get the current IRoutesCache provider
private readonly object _lock = new object(); /// </summary>
private Dictionary<int, string> _routes; internal class RoutesCache
private Dictionary<string, int> _nodeIds; {
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) /// <summary>
{ /// Constructor sets a custom provider if specified
_umbracoContext = umbracoContext; /// </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 /// <summary>
// at the moment this is done by library, which clears everything when content changed /// Singleton accessor
// /// </summary>
// but really, we should do some partial refreshes! public static RoutesCache Current
// otherwise, we could even cache 404 errors... {
} get { return Instance; }
}
public void Store(int nodeId, string route) /// <summary>
{ /// Get the current provider
if (_umbracoContext.InPreviewMode) /// </summary>
return; /// <returns></returns>
public IRoutesCache GetProvider()
lock (_lock) {
{ return _provider;
_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>();
}
}
}
} }

View File

@@ -3,10 +3,11 @@ using System.Linq;
namespace Umbraco.Web.Routing 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 internal class RoutingEnvironment
{ {
public RoutingEnvironment( public RoutingEnvironment(
@@ -28,13 +29,9 @@ namespace Umbraco.Web.Routing
}).ToList(); }).ToList();
} }
public IEnumerable<ILookup> Lookups public IEnumerable<ILookup> Lookups { get; private set; }
{
get;
private set;
}
public ILookupNotFound LookupNotFound { get; private set; } public ILookupNotFound LookupNotFound { get; private set; }
public ContentStore ContentStore { get; private set; } public ContentStore ContentStore { get; private set; }

View File

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

View File

@@ -49,7 +49,7 @@ namespace Umbraco.Web
/// <summary> /// <summary>
/// Gets the current Umbraco Context. /// Gets the current Umbraco Context.
/// </summary> /// </summary>
public static UmbracoContext Current public static UmbracoContext Current
{ {
get get
{ {
@@ -126,6 +126,16 @@ namespace Umbraco.Web
/// </summary> /// </summary>
internal DocumentRequest DocumentRequest { get; set; } 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> /// <summary>
/// Exposes the HttpContext for the current request /// Exposes the HttpContext for the current request
/// </summary> /// </summary>
@@ -193,29 +203,7 @@ namespace Umbraco.Web
&& !currentUrl.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)); // is not in admin UI && !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; UmbracoContext.Current = umbracoContext;
//create a content store //create a content store
var contentStore = new ContentStore(umbracoContext); var contentStore = new ContentStore(umbracoContext);
//create the routes cache
var routesCache = new RoutesCache(umbracoContext);
//create the nice urls //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!) //create the RoutingEnvironment (one per http request as it relies on the umbraco context!)
var routingEnvironment = new RoutingEnvironment( var routingEnvironment = new RoutingEnvironment(
ApplicationContext.Current.Plugins.ResolveLookups().ToArray(), ApplicationContext.Current.Plugins.ResolveLookups().ToArray(),
@@ -49,9 +47,15 @@ namespace Umbraco.Web
contentStore); contentStore);
// create the new document request which will cleanup the uri once and for all // create the new document request which will cleanup the uri once and for all
var docreq = new DocumentRequest(uri, routingEnvironment, umbracoContext, niceUrls); 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; 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!) //create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!)
var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext); var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext);