Introduced interface on PublishedRequest and moved interfaces to abstractions

This commit is contained in:
Bjarke Berg
2020-02-10 19:23:42 +01:00
parent 4b9d0941a4
commit e94921b384
35 changed files with 366 additions and 344 deletions

View File

@@ -70,7 +70,7 @@ namespace Umbraco.Web
/// <summary>
/// Gets/sets the PublishedRequest object
/// </summary>
PublishedRequest PublishedRequest { get; set; }
IPublishedRequest PublishedRequest { get; set; }
/// <summary>
/// Gets the variation context accessor.

View File

@@ -295,7 +295,7 @@ namespace Umbraco.Web.Routing
? currentUri.GetLeftPart(UriPartial.Authority) + domainName
: domainName;
var scheme = currentUri?.Scheme ?? Uri.UriSchemeHttp;
return new Uri(UriUtility.TrimPathEndSlash(UriUtility.StartWithScheme(name, scheme)));
return new Uri(UriUtilityCore.TrimPathEndSlash(UriUtilityCore.StartWithScheme(name, scheme)));
}
#endregion

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Routing
{
public interface IPublishedRequest
{
/// <summary>
/// Gets the UmbracoContext.
/// </summary>
IUmbracoContext UmbracoContext { get; }
/// <summary>
/// Gets or sets the cleaned up Uri used for routing.
/// </summary>
/// <remarks>The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.</remarks>
Uri Uri { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the Umbraco Backoffice should ignore a collision for this request.
/// </summary>
bool IgnorePublishedContentCollisions { get; set; }
/// <summary>
/// Gets or sets the requested content.
/// </summary>
/// <remarks>Setting the requested content clears <c>Template</c>.</remarks>
IPublishedContent PublishedContent { get; set; }
/// <summary>
/// Gets the initial requested content.
/// </summary>
/// <remarks>The initial requested content is the content that was found by the finders,
/// before anything such as 404, redirect... took place.</remarks>
IPublishedContent InitialPublishedContent { get; }
/// <summary>
/// Gets value indicating whether the current published content is the initial one.
/// </summary>
bool IsInitialPublishedContent { get; }
/// <summary>
/// Gets or sets a value indicating whether the current published content has been obtained
/// from the initial published content following internal redirections exclusively.
/// </summary>
/// <remarks>Used by PublishedContentRequestEngine.FindTemplate() to figure out whether to
/// apply the internal redirect or not, when content is not the initial content.</remarks>
bool IsInternalRedirectPublishedContent { get; }
/// <summary>
/// Gets a value indicating whether the content request has a content.
/// </summary>
bool HasPublishedContent { get; }
ITemplate TemplateModel { get; set; }
/// <summary>
/// Gets the alias of the template to use to display the requested content.
/// </summary>
string TemplateAlias { get; }
/// <summary>
/// Gets a value indicating whether the content request has a template.
/// </summary>
bool HasTemplate { get; }
void UpdateToNotFound();
/// <summary>
/// Gets or sets the content request's domain.
/// </summary>
/// <remarks>Is a DomainAndUri object ie a standard Domain plus the fully qualified uri. For example,
/// the <c>Domain</c> may contain "example.com" whereas the <c>Uri</c> will be fully qualified eg "http://example.com/".</remarks>
DomainAndUri Domain { get; set; }
/// <summary>
/// Gets a value indicating whether the content request has a domain.
/// </summary>
bool HasDomain { get; }
/// <summary>
/// Gets or sets the content request's culture.
/// </summary>
CultureInfo Culture { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the requested content could not be found.
/// </summary>
/// <remarks>This is set in the <c>PublishedContentRequestBuilder</c> and can also be used in
/// custom content finders or <c>Prepared</c> event handlers, where we want to allow developers
/// to indicate a request is 404 but not to cancel it.</remarks>
bool Is404 { get; set; }
/// <summary>
/// Gets a value indicating whether the content request triggers a redirect (permanent or not).
/// </summary>
bool IsRedirect { get; }
/// <summary>
/// Gets or sets a value indicating whether the redirect is permanent.
/// </summary>
bool IsRedirectPermanent { get; }
/// <summary>
/// Gets or sets the url to redirect to, when the content request triggers a redirect.
/// </summary>
string RedirectUrl { get; }
/// <summary>
/// Gets or sets the content request http response status code.
/// </summary>
/// <remarks>Does not actually set the http response status code, only registers that the response
/// should use the specified code. The code will or will not be used, in due time.</remarks>
int ResponseStatusCode { get; }
/// <summary>
/// Gets or sets the content request http response status description.
/// </summary>
/// <remarks>Does not actually set the http response status description, only registers that the response
/// should use the specified description. The description will or will not be used, in due time.</remarks>
string ResponseStatusDescription { get; }
/// <summary>
/// Gets or sets the <c>System.Web.HttpCacheability</c>
/// </summary>
// Note: we used to set a default value here but that would then be the default
// for ALL requests, we shouldn't overwrite it though if people are using [OutputCache] for example
// see: https://our.umbraco.com/forum/using-umbraco-and-getting-started/79715-output-cache-in-umbraco-752
//HttpCacheability Cacheability { get; set; }
/// <summary>
/// Gets or sets a list of Extensions to append to the Response.Cache object.
/// </summary>
List<string> CacheExtensions { get; set; }
/// <summary>
/// Gets or sets a dictionary of Headers to append to the Response object.
/// </summary>
Dictionary<string, string> Headers { get; set; }
bool CacheabilityNoCache { get; set; }
// PublishedContentHashtableConverter LegacyContentHashTable { get; set; }
/// <summary>
/// Prepares the request.
/// </summary>
void Prepare();
/// <summary>
/// Triggers the Preparing event.
/// </summary>
void OnPreparing();
/// <summary>
/// Triggers the Prepared event.
/// </summary>
void OnPrepared();
/// <summary>
/// Sets the requested content, following an internal redirect.
/// </summary>
/// <param name="content">The requested content.</param>
/// <remarks>Depending on <c>UmbracoSettings.InternalRedirectPreservesTemplate</c>, will
/// preserve or reset the template, if any.</remarks>
void SetInternalRedirectPublishedContent(IPublishedContent content);
/// <summary>
/// Indicates that the current PublishedContent is the initial one.
/// </summary>
void SetIsInitialPublishedContent();
/// <summary>
/// Tries to set the template to use to display the requested content.
/// </summary>
/// <param name="alias">The alias of the template.</param>
/// <returns>A value indicating whether a valid template with the specified alias was found.</returns>
/// <remarks>
/// <para>Successfully setting the template does refresh <c>RenderingEngine</c>.</para>
/// <para>If setting the template fails, then the previous template (if any) remains in place.</para>
/// </remarks>
bool TrySetTemplate(string alias);
/// <summary>
/// Sets the template to use to display the requested content.
/// </summary>
/// <param name="template">The template.</param>
/// <remarks>Setting the template does refresh <c>RenderingEngine</c>.</remarks>
void SetTemplate(ITemplate template);
/// <summary>
/// Resets the template.
/// </summary>
void ResetTemplate();
/// <summary>
/// Indicates that the content request should trigger a redirect (302).
/// </summary>
/// <param name="url">The url to redirect to.</param>
/// <remarks>Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.</remarks>
void SetRedirect(string url);
/// <summary>
/// Indicates that the content request should trigger a permanent redirect (301).
/// </summary>
/// <param name="url">The url to redirect to.</param>
/// <remarks>Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.</remarks>
void SetRedirectPermanent(string url);
/// <summary>
/// Indicates that the content request should trigger a redirect, with a specified status code.
/// </summary>
/// <param name="url">The url to redirect to.</param>
/// <param name="status">The status code (300-308).</param>
/// <remarks>Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.</remarks>
void SetRedirect(string url, int status);
/// <summary>
/// Sets the http response status code, along with an optional associated description.
/// </summary>
/// <param name="code">The http status code.</param>
/// <param name="description">The description.</param>
/// <remarks>Does not actually set the http response status code and description, only registers that
/// the response should use the specified code and description. The code and description will or will
/// not be used, in due time.</remarks>
void SetResponseStatus(int code, string description = null);
}
}

View File

@@ -0,0 +1,59 @@
using System;
using Umbraco.Core;
namespace Umbraco.Web
{
public static class UriUtilityCore
{
#region Uri string utilities
public static bool HasScheme(string uri)
{
return uri.IndexOf("://") > 0;
}
public static string StartWithScheme(string uri)
{
return StartWithScheme(uri, null);
}
public static string StartWithScheme(string uri, string scheme)
{
return HasScheme(uri) ? uri : String.Format("{0}://{1}", scheme ?? Uri.UriSchemeHttp, uri);
}
public static string EndPathWithSlash(string uri)
{
var pos1 = Math.Max(0, uri.IndexOf('?'));
var pos2 = Math.Max(0, uri.IndexOf('#'));
var pos = Math.Min(pos1, pos2);
var path = pos > 0 ? uri.Substring(0, pos) : uri;
path = path.EnsureEndsWith('/');
if (pos > 0)
path += uri.Substring(pos);
return path;
}
public static string TrimPathEndSlash(string uri)
{
var pos1 = Math.Max(0, uri.IndexOf('?'));
var pos2 = Math.Max(0, uri.IndexOf('#'));
var pos = Math.Min(pos1, pos2);
var path = pos > 0 ? uri.Substring(0, pos) : uri;
path = path.TrimEnd('/');
if (pos > 0)
path += uri.Substring(pos);
return path;
}
#endregion
}
}

View File

@@ -69,7 +69,7 @@ namespace Umbraco.Tests.Composing
var typesFound = typeFinder.FindClassesWithAttribute<TreeAttribute>(_assemblies);
Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree]
typesFound = typeFinder.FindClassesWithAttribute<TreeAttribute>(new[] { typeof (IUmbracoContext).Assembly });
typesFound = typeFinder.FindClassesWithAttribute<TreeAttribute>(new[] { typeof (UmbracoContext).Assembly });
Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree]
}

View File

@@ -46,7 +46,7 @@ namespace Umbraco.Tests.Composing
//typeof(TabPage).Assembly,
typeof(System.Web.Mvc.ActionResult).Assembly,
typeof(TypeFinder).Assembly,
typeof(IUmbracoContext).Assembly,
typeof(UmbracoContext).Assembly,
typeof(CheckBoxListPropertyEditor).Assembly
});

View File

@@ -39,21 +39,6 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsFalse(result);
}
[Test]
public void ConfigureRequest_Sets_UmbracoPage_When_Published_Content_Assigned()
{
var umbracoContext = GetUmbracoContext("/test");
var publishedRouter = CreatePublishedRouter();
var request = publishedRouter.CreateRequest(umbracoContext);
var content = GetPublishedContentMock();
request.Culture = new CultureInfo("en-AU");
request.PublishedContent = content.Object;
publishedRouter.ConfigureRequest(request);
Assert.IsNotNull(request.LegacyContentHashTable);
}
private Mock<IPublishedContent> GetPublishedContentMock()
{
var pc = new Mock<IPublishedContent>();

View File

@@ -4,7 +4,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs
{
internal class TestLastChanceFinder : IContentLastChanceFinder
{
public bool TryFindContent(PublishedRequest frequest)
public bool TryFindContent(IPublishedRequest frequest)
{
return false;
}

View File

@@ -17,21 +17,21 @@ namespace Umbraco.Web.Macros
/// <summary>
/// Legacy class used by macros which converts a published content item into a hashset of values
/// </summary>
internal class PublishedContentHashtableConverter
public class PublishedContentHashtableConverter
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentHashtableConverter"/> class for a published document request.
/// </summary>
/// <param name="frequest">The <see cref="PublishedRequest"/> pointing to the document.</param>
/// <param name="frequest">The <see cref="IPublishedRequest"/> pointing to the document.</param>
/// <param name="userService">The <see cref="IUserService"/>.</param>
/// <remarks>
/// The difference between creating the page with PublishedRequest vs an IPublishedContent item is
/// that the PublishedRequest takes into account how a template is assigned during the routing process whereas
/// with an IPublishedContent item, the template id is assigned purely based on the default.
/// </remarks>
internal PublishedContentHashtableConverter(PublishedRequest frequest, IUserService userService)
internal PublishedContentHashtableConverter(IPublishedRequest frequest, IUserService userService)
{
if (!frequest.HasPublishedContent)
throw new ArgumentException("Document request has no node.", nameof(frequest));

View File

@@ -99,7 +99,7 @@ namespace Umbraco.Web.Mvc
/// </summary>
/// <param name="request"></param>
/// <param name="filterContext"></param>
protected virtual void ConfigurePublishedContentRequest(PublishedRequest request, ActionExecutedContext filterContext)
protected virtual void ConfigurePublishedContentRequest(IPublishedRequest request, ActionExecutedContext filterContext)
{
if (_contentId.HasValue)
{

View File

@@ -18,7 +18,7 @@ namespace Umbraco.Web.Mvc
[ModelBindingExceptionFilter]
public class RenderMvcController : UmbracoController, IRenderMvcController
{
private PublishedRequest _publishedRequest;
private IPublishedRequest _publishedRequest;
public RenderMvcController()
{
@@ -44,7 +44,7 @@ namespace Umbraco.Web.Mvc
/// <summary>
/// Gets the current published content request.
/// </summary>
protected internal virtual PublishedRequest PublishedRequest
protected internal virtual IPublishedRequest PublishedRequest
{
get
{
@@ -54,7 +54,7 @@ namespace Umbraco.Web.Mvc
{
throw new InvalidOperationException("DataTokens must contain an 'umbraco-doc-request' key with a PublishedRequest object");
}
_publishedRequest = (PublishedRequest)RouteData.DataTokens[Core.Constants.Web.PublishedDocumentRequestDataToken];
_publishedRequest = (IPublishedRequest)RouteData.DataTokens[Core.Constants.Web.PublishedDocumentRequestDataToken];
return _publishedRequest;
}
}

View File

@@ -86,7 +86,7 @@ namespace Umbraco.Web.Mvc
/// <param name="contentModel"></param>
/// <param name="requestContext"></param>
/// <param name="frequest"></param>
internal void SetupRouteDataForRequest(ContentModel contentModel, RequestContext requestContext, PublishedRequest frequest)
internal void SetupRouteDataForRequest(ContentModel contentModel, RequestContext requestContext, IPublishedRequest frequest)
{
//put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, contentModel); //required for the ContentModelBinder and view engine
@@ -241,7 +241,7 @@ namespace Umbraco.Web.Mvc
/// <param name="requestContext"></param>
/// <param name="request"></param>
/// <returns></returns>
internal virtual RouteDefinition GetUmbracoRouteDefinition(RequestContext requestContext, PublishedRequest request)
internal virtual RouteDefinition GetUmbracoRouteDefinition(RequestContext requestContext, IPublishedRequest request)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (request == null) throw new ArgumentNullException(nameof(request));
@@ -306,7 +306,7 @@ namespace Umbraco.Web.Mvc
return def;
}
internal IHttpHandler GetHandlerOnMissingTemplate(PublishedRequest request)
internal IHttpHandler GetHandlerOnMissingTemplate(IPublishedRequest request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
@@ -331,7 +331,7 @@ namespace Umbraco.Web.Mvc
/// </summary>
/// <param name="requestContext"></param>
/// <param name="request"></param>
internal IHttpHandler GetHandlerForRoute(RequestContext requestContext, PublishedRequest request)
internal IHttpHandler GetHandlerForRoute(RequestContext requestContext, IPublishedRequest request)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (request == null) throw new ArgumentNullException(nameof(request));

View File

@@ -21,7 +21,7 @@ namespace Umbraco.Web.Mvc
/// <summary>
/// Everything related to the current content request including the requested content
/// </summary>
public PublishedRequest PublishedRequest { get; set; }
public IPublishedRequest PublishedRequest { get; set; }
/// <summary>
/// Gets/sets whether the current request has a hijacked route/user controller routed for it

View File

@@ -56,7 +56,7 @@ namespace Umbraco.Web.Mvc
/// <summary>
/// Gets the public content request.
/// </summary>
internal PublishedRequest PublishedRequest
internal IPublishedRequest PublishedRequest
{
get
{
@@ -67,11 +67,11 @@ namespace Umbraco.Web.Mvc
// try view context
if (ViewContext.RouteData.DataTokens.ContainsKey(token))
return (PublishedRequest) ViewContext.RouteData.DataTokens.GetRequiredObject(token);
return (IPublishedRequest) ViewContext.RouteData.DataTokens.GetRequiredObject(token);
// child action, try parent view context
if (ViewContext.IsChildAction && ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey(token))
return (PublishedRequest) ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject(token);
return (IPublishedRequest) ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject(token);
// fallback to UmbracoContext
return UmbracoContext.PublishedRequest;

View File

@@ -90,7 +90,7 @@ namespace Umbraco.Web.Mvc
protected abstract IPublishedContent FindContent(RequestContext requestContext, IUmbracoContext umbracoContext);
protected virtual void PreparePublishedContentRequest(PublishedRequest request)
protected virtual void PreparePublishedContentRequest(IPublishedRequest request)
{
PublishedRouter.PrepareRequest(request);
}

View File

@@ -32,7 +32,7 @@ namespace Umbraco.Web.Routing
/// </summary>
/// <param name="frequest">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
public bool TryFindContent(PublishedRequest frequest)
public bool TryFindContent(IPublishedRequest frequest)
{
_logger.Debug<ContentFinderByConfigured404>("Looking for a page to handle 404.");

View File

@@ -31,7 +31,7 @@ namespace Umbraco.Web.Routing
/// </summary>
/// <param name="frequest">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
public bool TryFindContent(PublishedRequest frequest)
public bool TryFindContent(IPublishedRequest frequest)
{
if (frequest.UmbracoContext != null && frequest.UmbracoContext.InPreviewMode == false

View File

@@ -16,7 +16,7 @@
_httpContextAccessor = httpContextAccessor;
}
public bool TryFindContent(PublishedRequest frequest)
public bool TryFindContent(IPublishedRequest frequest)
{
int pageId;
if (int.TryParse(_httpContextAccessor.HttpContext.Request["umbPageID"], out pageId))

View File

@@ -30,7 +30,7 @@ namespace Umbraco.Web.Routing
/// <param name="frequest">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
/// <remarks>Optionally, can also assign the template or anything else on the document request, although that is not required.</remarks>
public bool TryFindContent(PublishedRequest frequest)
public bool TryFindContent(IPublishedRequest frequest)
{
var route = frequest.HasDomain
? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded())
@@ -63,7 +63,8 @@ namespace Umbraco.Web.Routing
// See http://issues.umbraco.org/issue/U4-8361#comment=67-30532
// Setting automatic 301 redirects to not be cached because browsers cache these very aggressively which then leads
// to problems if you rename a page back to it's original name or create a new page with the original name
frequest.Cacheability = HttpCacheability.NoCache;
//frequest.Cacheability = HttpCacheability.NoCache;
frequest.CacheabilityNoCache = true;
frequest.CacheExtensions = new List<string> { "no-store, must-revalidate" };
frequest.Headers = new Dictionary<string, string> { { "Pragma", "no-cache" }, { "Expires", "0" } };

View File

@@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing
/// </summary>
/// <param name="frequest">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
public virtual bool TryFindContent(PublishedRequest frequest)
public virtual bool TryFindContent(IPublishedRequest frequest)
{
string route;
if (frequest.HasDomain)
@@ -42,7 +42,7 @@ namespace Umbraco.Web.Routing
/// <param name="docreq">The document request.</param>
/// <param name="route">The route.</param>
/// <returns>The document node, or null.</returns>
protected IPublishedContent FindContent(PublishedRequest docreq, string route)
protected IPublishedContent FindContent(IPublishedRequest docreq, string route)
{
if (docreq == null) throw new System.ArgumentNullException(nameof(docreq));

View File

@@ -30,7 +30,7 @@ namespace Umbraco.Web.Routing
/// </summary>
/// <param name="frequest">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
public bool TryFindContent(PublishedRequest frequest)
public bool TryFindContent(IPublishedRequest frequest)
{
IPublishedContent node = null;

View File

@@ -32,7 +32,7 @@ namespace Umbraco.Web.Routing
/// <param name="frequest">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
/// <remarks>If successful, also assigns the template.</remarks>
public override bool TryFindContent(PublishedRequest frequest)
public override bool TryFindContent(IPublishedRequest frequest)
{
IPublishedContent node = null;
var path = frequest.Uri.GetAbsolutePathDecoded();

View File

@@ -11,6 +11,6 @@ namespace Umbraco.Web.Routing
/// <param name="request">The <c>PublishedRequest</c>.</param>
/// <returns>A value indicating whether an Umbraco document was found and assigned.</returns>
/// <remarks>Optionally, can also assign the template or anything else on the document request, although that is not required.</remarks>
bool TryFindContent(PublishedRequest request);
bool TryFindContent(IPublishedRequest request);
}
}

View File

@@ -17,21 +17,21 @@ namespace Umbraco.Web.Routing
/// <param name="umbracoContext">The current Umbraco context.</param>
/// <param name="uri">The (optional) request Uri.</param>
/// <returns>A published request.</returns>
PublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null);
IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null);
/// <summary>
/// Prepares a request for rendering.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>A value indicating whether the request was successfully prepared and can be rendered.</returns>
bool PrepareRequest(PublishedRequest request);
bool PrepareRequest(IPublishedRequest request);
/// <summary>
/// Tries to route a request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>A value indicating whether the request can be routed to a document.</returns>
bool TryRouteRequest(PublishedRequest request);
bool TryRouteRequest(IPublishedRequest request);
/// <summary>
/// Gets a template.
@@ -49,6 +49,6 @@ namespace Umbraco.Web.Routing
/// the request, for whatever reason, and wants to force it to be re-routed
/// and rendered as if no document were found (404).</para>
/// </remarks>
void UpdateRequestToNotFound(PublishedRequest request);
void UpdateRequestToNotFound(IPublishedRequest request);
}
}

View File

@@ -12,213 +12,6 @@ using Umbraco.Core.Configuration.UmbracoSettings;
namespace Umbraco.Web.Routing
{
public interface IPublishedRequest
{
/// <summary>
/// Gets the UmbracoContext.
/// </summary>
IUmbracoContext UmbracoContext { get; }
/// <summary>
/// Gets or sets the cleaned up Uri used for routing.
/// </summary>
/// <remarks>The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.</remarks>
Uri Uri { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the Umbraco Backoffice should ignore a collision for this request.
/// </summary>
bool IgnorePublishedContentCollisions { get; set; }
/// <summary>
/// Gets or sets the requested content.
/// </summary>
/// <remarks>Setting the requested content clears <c>Template</c>.</remarks>
IPublishedContent PublishedContent { get; set; }
/// <summary>
/// Gets the initial requested content.
/// </summary>
/// <remarks>The initial requested content is the content that was found by the finders,
/// before anything such as 404, redirect... took place.</remarks>
IPublishedContent InitialPublishedContent { get; }
/// <summary>
/// Gets value indicating whether the current published content is the initial one.
/// </summary>
bool IsInitialPublishedContent { get; }
/// <summary>
/// Gets or sets a value indicating whether the current published content has been obtained
/// from the initial published content following internal redirections exclusively.
/// </summary>
/// <remarks>Used by PublishedContentRequestEngine.FindTemplate() to figure out whether to
/// apply the internal redirect or not, when content is not the initial content.</remarks>
bool IsInternalRedirectPublishedContent { get; }
/// <summary>
/// Gets a value indicating whether the content request has a content.
/// </summary>
bool HasPublishedContent { get; }
/// <summary>
/// Gets the alias of the template to use to display the requested content.
/// </summary>
string TemplateAlias { get; }
/// <summary>
/// Gets a value indicating whether the content request has a template.
/// </summary>
bool HasTemplate { get; }
/// <summary>
/// Gets or sets the content request's domain.
/// </summary>
/// <remarks>Is a DomainAndUri object ie a standard Domain plus the fully qualified uri. For example,
/// the <c>Domain</c> may contain "example.com" whereas the <c>Uri</c> will be fully qualified eg "http://example.com/".</remarks>
DomainAndUri Domain { get; set; }
/// <summary>
/// Gets a value indicating whether the content request has a domain.
/// </summary>
bool HasDomain { get; }
/// <summary>
/// Gets or sets the content request's culture.
/// </summary>
CultureInfo Culture { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the requested content could not be found.
/// </summary>
/// <remarks>This is set in the <c>PublishedContentRequestBuilder</c> and can also be used in
/// custom content finders or <c>Prepared</c> event handlers, where we want to allow developers
/// to indicate a request is 404 but not to cancel it.</remarks>
bool Is404 { get; set; }
/// <summary>
/// Gets a value indicating whether the content request triggers a redirect (permanent or not).
/// </summary>
bool IsRedirect { get; }
/// <summary>
/// Gets or sets a value indicating whether the redirect is permanent.
/// </summary>
bool IsRedirectPermanent { get; }
/// <summary>
/// Gets or sets the url to redirect to, when the content request triggers a redirect.
/// </summary>
string RedirectUrl { get; }
/// <summary>
/// Gets or sets the content request http response status code.
/// </summary>
/// <remarks>Does not actually set the http response status code, only registers that the response
/// should use the specified code. The code will or will not be used, in due time.</remarks>
int ResponseStatusCode { get; }
/// <summary>
/// Gets or sets the content request http response status description.
/// </summary>
/// <remarks>Does not actually set the http response status description, only registers that the response
/// should use the specified description. The description will or will not be used, in due time.</remarks>
string ResponseStatusDescription { get; }
/// <summary>
/// Gets or sets the <c>System.Web.HttpCacheability</c>
/// </summary>
// Note: we used to set a default value here but that would then be the default
// for ALL requests, we shouldn't overwrite it though if people are using [OutputCache] for example
// see: https://our.umbraco.com/forum/using-umbraco-and-getting-started/79715-output-cache-in-umbraco-752
HttpCacheability Cacheability { get; set; }
/// <summary>
/// Gets or sets a list of Extensions to append to the Response.Cache object.
/// </summary>
List<string> CacheExtensions { get; set; }
/// <summary>
/// Gets or sets a dictionary of Headers to append to the Response object.
/// </summary>
Dictionary<string, string> Headers { get; set; }
/// <summary>
/// Prepares the request.
/// </summary>
void Prepare();
/// <summary>
/// Sets the requested content, following an internal redirect.
/// </summary>
/// <param name="content">The requested content.</param>
/// <remarks>Depending on <c>UmbracoSettings.InternalRedirectPreservesTemplate</c>, will
/// preserve or reset the template, if any.</remarks>
void SetInternalRedirectPublishedContent(IPublishedContent content);
/// <summary>
/// Indicates that the current PublishedContent is the initial one.
/// </summary>
void SetIsInitialPublishedContent();
/// <summary>
/// Tries to set the template to use to display the requested content.
/// </summary>
/// <param name="alias">The alias of the template.</param>
/// <returns>A value indicating whether a valid template with the specified alias was found.</returns>
/// <remarks>
/// <para>Successfully setting the template does refresh <c>RenderingEngine</c>.</para>
/// <para>If setting the template fails, then the previous template (if any) remains in place.</para>
/// </remarks>
bool TrySetTemplate(string alias);
/// <summary>
/// Sets the template to use to display the requested content.
/// </summary>
/// <param name="template">The template.</param>
/// <remarks>Setting the template does refresh <c>RenderingEngine</c>.</remarks>
void SetTemplate(ITemplate template);
/// <summary>
/// Resets the template.
/// </summary>
void ResetTemplate();
/// <summary>
/// Indicates that the content request should trigger a redirect (302).
/// </summary>
/// <param name="url">The url to redirect to.</param>
/// <remarks>Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.</remarks>
void SetRedirect(string url);
/// <summary>
/// Indicates that the content request should trigger a permanent redirect (301).
/// </summary>
/// <param name="url">The url to redirect to.</param>
/// <remarks>Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.</remarks>
void SetRedirectPermanent(string url);
/// <summary>
/// Indicates that the content request should trigger a redirect, with a specified status code.
/// </summary>
/// <param name="url">The url to redirect to.</param>
/// <param name="status">The status code (300-308).</param>
/// <remarks>Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.</remarks>
void SetRedirect(string url, int status);
/// <summary>
/// Sets the http response status code, along with an optional associated description.
/// </summary>
/// <param name="code">The http status code.</param>
/// <param name="description">The description.</param>
/// <remarks>Does not actually set the http response status code and description, only registers that
/// the response should use the specified code and description. The code and description will or will
/// not be used, in due time.</remarks>
void SetResponseStatus(int code, string description = null);
}
/// <summary>
/// Represents a request for one specified Umbraco IPublishedContent to be rendered
@@ -240,7 +33,7 @@ namespace Umbraco.Web.Routing
private PublishedContentHashtableConverter _umbracoPage; // legacy
/// <summary>
/// Initializes a new instance of the <see cref="PublishedRequest"/> class.
/// Initializes a new instance of the <see cref="IPublishedRequest"/> class.
/// </summary>
/// <param name="publishedRouter">The published router.</param>
/// <param name="umbracoContext">The Umbraco context.</param>
@@ -274,12 +67,14 @@ namespace Umbraco.Web.Routing
}
// utility for ensuring it is ok to set some properties
private void EnsureWriteable()
public void EnsureWriteable()
{
if (_readonly)
throw new InvalidOperationException("Cannot modify a PublishedRequest once it is read-only.");
}
public bool CacheabilityNoCache { get; set; }
/// <summary>
/// Prepares the request.
/// </summary>
@@ -314,7 +109,7 @@ namespace Umbraco.Web.Routing
/// <summary>
/// Triggers the Preparing event.
/// </summary>
internal void OnPreparing()
public void OnPreparing()
{
Preparing?.Invoke(this, EventArgs.Empty);
_readonlyUri = true;
@@ -323,7 +118,7 @@ namespace Umbraco.Web.Routing
/// <summary>
/// Triggers the Prepared event.
/// </summary>
internal void OnPrepared()
public void OnPrepared()
{
Prepared?.Invoke(this, EventArgs.Empty);
@@ -439,7 +234,7 @@ namespace Umbraco.Web.Routing
/// <summary>
/// Gets or sets the template model to use to display the requested content.
/// </summary>
internal ITemplate TemplateModel { get; set; }
public ITemplate TemplateModel { get; set; }
/// <summary>
/// Gets the alias of the template to use to display the requested content.
@@ -501,7 +296,7 @@ namespace Umbraco.Web.Routing
/// </summary>
public bool HasTemplate => TemplateModel != null;
internal void UpdateToNotFound()
public void UpdateToNotFound()
{
var __readonly = _readonly;
_readonly = false;
@@ -672,7 +467,7 @@ namespace Umbraco.Web.Routing
// Note: we used to set a default value here but that would then be the default
// for ALL requests, we shouldn't overwrite it though if people are using [OutputCache] for example
// see: https://our.umbraco.com/forum/using-umbraco-and-getting-started/79715-output-cache-in-umbraco-752
public HttpCacheability Cacheability { get; set; }
//public HttpCacheability Cacheability { get; set; }
/// <summary>
/// Gets or sets a list of Extensions to append to the Response.Cache object.
@@ -690,7 +485,7 @@ namespace Umbraco.Web.Routing
// for legacy/webforms/macro code -
// TODO: get rid of it eventually
internal PublishedContentHashtableConverter LegacyContentHashTable
public PublishedContentHashtableConverter LegacyContentHashTable
{
get
{

View File

@@ -58,7 +58,7 @@ namespace Umbraco.Web.Routing
}
/// <inheritdoc />
public PublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null)
public IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null)
{
return new PublishedRequest(this, umbracoContext, _umbracoSettingsSection, uri ?? umbracoContext.CleanedUmbracoUrl);
}
@@ -66,7 +66,7 @@ namespace Umbraco.Web.Routing
#region Request
/// <inheritdoc />
public bool TryRouteRequest(PublishedRequest request)
public bool TryRouteRequest(IPublishedRequest request)
{
// disabled - is it going to change the routing?
//_pcr.OnPreparing();
@@ -97,7 +97,7 @@ namespace Umbraco.Web.Routing
}
/// <inheritdoc />
public bool PrepareRequest(PublishedRequest request)
public bool PrepareRequest(IPublishedRequest request)
{
// note - at that point the original legacy module did something do handle IIS custom 404 errors
// ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support
@@ -168,7 +168,7 @@ namespace Umbraco.Web.Routing
/// This method logic has been put into it's own method in case developers have created a custom PCR or are assigning their own values
/// but need to finalize it themselves.
/// </remarks>
public bool ConfigureRequest(PublishedRequest frequest)
public bool ConfigureRequest(IPublishedRequest frequest)
{
if (frequest.HasPublishedContent == false)
{
@@ -200,13 +200,13 @@ namespace Umbraco.Web.Routing
// assign the legacy page back to the request
// handlers like default.aspx will want it and most macros currently need it
frequest.LegacyContentHashTable = new PublishedContentHashtableConverter(frequest, _userService);
// frequest.LegacyContentHashTable = new PublishedContentHashtableConverter(frequest, _userService);
return true;
}
/// <inheritdoc />
public void UpdateRequestToNotFound(PublishedRequest request)
public void UpdateRequestToNotFound(IPublishedRequest request)
{
// clear content
var content = request.PublishedContent;
@@ -240,7 +240,7 @@ namespace Umbraco.Web.Routing
// assign the legacy page back to the docrequest
// handlers like default.aspx will want it and most macros currently need it
request.LegacyContentHashTable = new PublishedContentHashtableConverter(request, _userService);
// request.LegacyContentHashTable = new PublishedContentHashtableConverter(request, _userService);
}
#endregion
@@ -251,7 +251,7 @@ namespace Umbraco.Web.Routing
/// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly.
/// </summary>
/// <returns>A value indicating whether a domain was found.</returns>
internal bool FindDomain(PublishedRequest request)
internal bool FindDomain(IPublishedRequest request)
{
const string tracePrefix = "FindDomain: ";
@@ -322,7 +322,7 @@ namespace Umbraco.Web.Routing
/// <summary>
/// Looks for wildcard domains in the path and updates <c>Culture</c> accordingly.
/// </summary>
internal void HandleWildcardDomains(PublishedRequest request)
internal void HandleWildcardDomains(IPublishedRequest request)
{
const string tracePrefix = "HandleWildcardDomains: ";
@@ -382,7 +382,7 @@ namespace Umbraco.Web.Routing
/// Finds the Umbraco document (if any) matching the request, and updates the PublishedRequest accordingly.
/// </summary>
/// <returns>A value indicating whether a document and template were found.</returns>
private void FindPublishedContentAndTemplate(PublishedRequest request)
private void FindPublishedContentAndTemplate(IPublishedRequest request)
{
_logger.Debug<PublishedRouter>("FindPublishedContentAndTemplate: Path={UriAbsolutePath}", request.Uri.AbsolutePath);
@@ -412,7 +412,7 @@ namespace Umbraco.Web.Routing
/// Tries to find the document matching the request, by running the IPublishedContentFinder instances.
/// </summary>
/// <exception cref="InvalidOperationException">There is no finder collection.</exception>
internal void FindPublishedContent(PublishedRequest request)
internal void FindPublishedContent(IPublishedRequest request)
{
const string tracePrefix = "FindPublishedContent: ";
@@ -444,7 +444,7 @@ namespace Umbraco.Web.Routing
/// Handles "not found", internal redirects, access validation...
/// things that must be handled in one place because they can create loops
/// </remarks>
private void HandlePublishedContent(PublishedRequest request)
private void HandlePublishedContent(IPublishedRequest request)
{
// because these might loop, we have to have some sort of infinite loop detection
int i = 0, j = 0;
@@ -503,7 +503,7 @@ namespace Umbraco.Web.Routing
/// <para>Redirecting to a different site root and/or culture will not pick the new site root nor the new culture.</para>
/// <para>As per legacy, if the redirect does not work, we just ignore it.</para>
/// </remarks>
private bool FollowInternalRedirects(PublishedRequest request)
private bool FollowInternalRedirects(IPublishedRequest request)
{
if (request.PublishedContent == null)
throw new InvalidOperationException("There is no PublishedContent.");
@@ -565,7 +565,7 @@ namespace Umbraco.Web.Routing
/// Ensures that access to current node is permitted.
/// </summary>
/// <remarks>Redirecting to a different site root and/or culture will not pick the new site root nor the new culture.</remarks>
private void EnsurePublishedContentAccess(PublishedRequest request)
private void EnsurePublishedContentAccess(IPublishedRequest request)
{
if (request.PublishedContent == null)
throw new InvalidOperationException("There is no PublishedContent.");
@@ -635,7 +635,7 @@ namespace Umbraco.Web.Routing
/// <summary>
/// Finds a template for the current node, if any.
/// </summary>
private void FindTemplate(PublishedRequest request)
private void FindTemplate(IPublishedRequest request)
{
// NOTE: at the moment there is only 1 way to find a template, and then ppl must
// use the Prepared event to change the template if they wish. Should we also
@@ -665,7 +665,7 @@ namespace Umbraco.Web.Routing
if (request.HasTemplate)
{
_logger.Debug<PublishedRequest>("FindTemplate: Has a template already, and no alternate template.");
_logger.Debug<IPublishedRequest>("FindTemplate: Has a template already, and no alternate template.");
return;
}
@@ -759,7 +759,7 @@ namespace Umbraco.Web.Routing
/// Follows external redirection through <c>umbracoRedirect</c> document property.
/// </summary>
/// <remarks>As per legacy, if the redirect does not work, we just ignore it.</remarks>
private void FollowExternalRedirect(PublishedRequest request)
private void FollowExternalRedirect(IPublishedRequest request)
{
if (request.HasPublishedContent == false) return;

View File

@@ -266,7 +266,7 @@ namespace Umbraco.Web.Routing
return _qualifiedSites[current.Scheme] = _sites
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Select(d => new Uri(UriUtility.StartWithScheme(d, current.Scheme)).GetLeftPart(UriPartial.Authority)).ToArray()
kvp => kvp.Value.Select(d => new Uri(UriUtilityCore.StartWithScheme(d, current.Scheme)).GetLeftPart(UriPartial.Authority)).ToArray()
);
// .ToDictionary will evaluate and create the dictionary immediately

View File

@@ -117,7 +117,7 @@ namespace Umbraco.Web.Templates
}
private void ExecuteTemplateRendering(TextWriter sw, PublishedRequest request)
private void ExecuteTemplateRendering(TextWriter sw, IPublishedRequest request)
{
//NOTE: Before we used to build up the query strings here but this is not necessary because when we do a
// Server.Execute in the TemplateRenderer, we pass in a 'true' to 'preserveForm' which automatically preserves all current
@@ -172,7 +172,7 @@ namespace Umbraco.Web.Templates
return newWriter.ToString();
}
private void SetNewItemsOnContextObjects(PublishedRequest request)
private void SetNewItemsOnContextObjects(IPublishedRequest request)
{
//now, set the new ones for this page execution
_httpContextAccessor.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null;
@@ -182,7 +182,7 @@ namespace Umbraco.Web.Templates
/// <summary>
/// Save all items that we know are used for rendering execution to variables so we can restore after rendering
/// </summary>
private void SaveExistingItems(out PublishedRequest oldPublishedRequest, out object oldAltTemplate)
private void SaveExistingItems(out IPublishedRequest oldPublishedRequest, out object oldAltTemplate)
{
//Many objects require that these legacy items are in the http context items... before we render this template we need to first
//save the values in them so that we can re-set them after we render so the rest of the execution works as per normal
@@ -193,7 +193,7 @@ namespace Umbraco.Web.Templates
/// <summary>
/// Restores all items back to their context's to continue normal page rendering execution
/// </summary>
private void RestoreItems(PublishedRequest oldPublishedRequest, object oldAltTemplate)
private void RestoreItems(IPublishedRequest oldPublishedRequest, object oldAltTemplate)
{
_umbracoContextAccessor.UmbracoContext.PublishedRequest = oldPublishedRequest;
_httpContextAccessor.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = oldAltTemplate;

View File

@@ -654,10 +654,8 @@
<Compile Include="Routing\AliasUrlProvider.cs" />
<Compile Include="Routing\ContentFinderByConfigured404.cs" />
<Compile Include="Routing\ContentFinderByPageIdQuery.cs" />
<Compile Include="Routing\ISiteDomainHelper.cs" />
<Compile Include="Routing\RoutableAttemptEventArgs.cs" />
<Compile Include="Routing\DefaultUrlProvider.cs" />
<Compile Include="Routing\DomainAndUri.cs" />
<Compile Include="Routing\IUrlProvider.cs" />
<Compile Include="Routing\SiteDomainHelper.cs" />
<Compile Include="Routing\EnsureRoutableOutcome.cs" />
@@ -694,7 +692,6 @@
<Compile Include="Routing\ContentFinderByUrlAlias.cs" />
<Compile Include="Routing\ContentFinderByIdPath.cs" />
<Compile Include="TypeLoaderExtensions.cs" />
<Compile Include="Routing\DomainUtilities.cs" />
<Compile Include="Routing\PublishedRequest.cs" />
<Compile Include="Routing\IContentFinder.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
@@ -704,7 +701,6 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="UmbracoApplication.cs" />
<Compile Include="IUmbracoContext.cs" />
<Compile Include="UmbracoInjectedModule.cs" />
<Compile Include="UriUtility.cs" />
<Compile Include="Web References\org.umbraco.update\Reference.cs">

View File

@@ -139,7 +139,7 @@ namespace Umbraco.Web
/// <summary>
/// Gets/sets the PublishedRequest object
/// </summary>
public PublishedRequest PublishedRequest { get; set; }
public IPublishedRequest PublishedRequest { get; set; }
/// <summary>
/// Exposes the HttpContext for the current request

View File

@@ -273,7 +273,7 @@ namespace Umbraco.Web
/// </summary>
/// <param name="context"></param>
/// <param name="pcr"> </param>
private void RewriteToUmbracoHandler(HttpContextBase context, PublishedRequest pcr)
private void RewriteToUmbracoHandler(HttpContextBase context, IPublishedRequest pcr)
{
// NOTE: we do not want to use TransferRequest even though many docs say it is better with IIS7, turns out this is
// not what we need. The purpose of TransferRequest is to ensure that .net processes all of the rules for the newly

View File

@@ -45,7 +45,7 @@ namespace Umbraco.Web
// returns a value indicating whether redirection took place and the request has
// been completed - because we don't want to Response.End() here to terminate
// everything properly.
internal static bool HandleHttpResponseStatus(HttpContextBase context, PublishedRequest pcr, ILogger logger)
internal static bool HandleHttpResponseStatus(HttpContextBase context, IPublishedRequest pcr, ILogger logger)
{
var end = false;
var response = context.Response;
@@ -55,8 +55,8 @@ namespace Umbraco.Web
pcr.Is404 ? "true" : "false",
pcr.ResponseStatusCode);
if(pcr.Cacheability != default)
response.Cache.SetCacheability(pcr.Cacheability);
if(pcr.CacheabilityNoCache)
response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
foreach (var cacheExtension in pcr.CacheExtensions)
response.Cache.AppendCacheExtension(cacheExtension);

View File

@@ -177,54 +177,6 @@ namespace Umbraco.Web
#endregion
#region Uri string utilities
public static bool HasScheme(string uri)
{
return uri.IndexOf("://") > 0;
}
public static string StartWithScheme(string uri)
{
return StartWithScheme(uri, null);
}
public static string StartWithScheme(string uri, string scheme)
{
return HasScheme(uri) ? uri : String.Format("{0}://{1}", scheme ?? Uri.UriSchemeHttp, uri);
}
public static string EndPathWithSlash(string uri)
{
var pos1 = Math.Max(0, uri.IndexOf('?'));
var pos2 = Math.Max(0, uri.IndexOf('#'));
var pos = Math.Min(pos1, pos2);
var path = pos > 0 ? uri.Substring(0, pos) : uri;
path = path.EnsureEndsWith('/');
if (pos > 0)
path += uri.Substring(pos);
return path;
}
public static string TrimPathEndSlash(string uri)
{
var pos1 = Math.Max(0, uri.IndexOf('?'));
var pos2 = Math.Max(0, uri.IndexOf('#'));
var pos = Math.Min(pos1, pos2);
var path = pos > 0 ? uri.Substring(0, pos) : uri;
path = path.TrimEnd('/');
if (pos > 0)
path += uri.Substring(pos);
return path;
}
#endregion
/// <summary>
/// Returns an full url with the host, port, etc...