re-enables the weird IgnorePublishedContentCollisions, simplifies IPublishedRouter interface (more flexible with request options),

This commit is contained in:
Shannon
2021-01-08 10:42:57 +11:00
parent b017ed0b1b
commit b4922d2685
11 changed files with 116 additions and 42 deletions

View File

@@ -6,7 +6,9 @@ using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Routing
{
/// <summary>
/// The result of Umbraco routing built with the <see cref="IPublishedRequestBuilder"/>
/// </summary>
public interface IPublishedRequest
{
/// <summary>
@@ -15,11 +17,6 @@ namespace Umbraco.Web.Routing
/// <remarks>The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.</remarks>
Uri Uri { get; }
/// <summary>
/// Gets a value indicating whether the Umbraco Backoffice should ignore a collision for this request.
/// </summary>
bool IgnorePublishedContentCollisions { get; }
/// <summary>
/// Gets a value indicating the requested content.
/// </summary>
@@ -73,8 +70,22 @@ namespace Umbraco.Web.Routing
IReadOnlyDictionary<string, string> Headers { get; }
/// <summary>
/// Gets a value indicating if the no-cache value should be added to the Cache-Control header
/// Gets a value indicating whether the no-cache value should be added to the Cache-Control header
/// </summary>
bool SetNoCacheHeader { get; }
/// <summary>
/// Gets a value indicating whether the Umbraco Backoffice should ignore a collision for this request.
/// </summary>
/// <remarks>
/// <para>This is an uncommon API used for edge cases with complex routing and would be used
/// by developers to configure the request to disable collision checks in <see cref="UrlProviderExtensions"/>.</para>
/// <para>This flag is based on previous Umbraco versions but it is not clear how this flag can be set by developers since
/// collission checking only occurs in the back office which is launched by <see cref="IPublishedRouter.TryRouteRequestAsync(IPublishedRequestBuilder)"/>
/// for which events do not execute.</para>
/// <para>More can be read about this setting here: https://github.com/umbraco/Umbraco-CMS/pull/2148, https://issues.umbraco.org/issue/U4-10345
/// but it's still unclear how this was used.</para>
/// </remarks>
bool IgnorePublishedContentCollisions { get; }
}
}

View File

@@ -138,5 +138,19 @@ namespace Umbraco.Web.Routing
/// Sets a dictionary of Headers to append to the Response object.
/// </summary>
IPublishedRequestBuilder SetHeaders(IReadOnlyDictionary<string, string> headers);
/// <summary>
/// Can be called to configure the <see cref="IPublishedRequest"/> result to ignore URL collisions
/// </summary>
/// <remarks>
/// <para>This is an uncommon API used for edge cases with complex routing and would be used
/// by developers to configure the request to disable collision checks in <see cref="UrlProviderExtensions"/>.</para>
/// <para>This flag is based on previous Umbraco versions but it is not clear how this flag can be set by developers since
/// collission checking only occurs in the back office which is launched by <see cref="IPublishedRouter.TryRouteRequestAsync(IPublishedRequestBuilder)"/>
/// for which events do not execute.</para>
/// <para>More can be read about this setting here: https://github.com/umbraco/Umbraco-CMS/pull/2148, https://issues.umbraco.org/issue/U4-10345
/// but it's still unclear how this was used.</para>
/// </remarks>
void IgnorePublishedContentCollisions();
}
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Umbraco.Core.Models;
namespace Umbraco.Web.Routing
{
@@ -10,8 +8,6 @@ namespace Umbraco.Web.Routing
/// </summary>
public interface IPublishedRouter
{
// TODO: consider this and UmbracoRouteValueTransformer - move some code around?
/// <summary>
/// Creates a published request.
/// </summary>
@@ -20,18 +16,12 @@ namespace Umbraco.Web.Routing
Task<IPublishedRequestBuilder> CreateRequestAsync(Uri uri);
/// <summary>
/// Prepares a request for rendering.
/// Sends a <see cref="IPublishedRequestBuilder"/> through the routing pipeline and builds a result.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>A value indicating whether the request was successfully prepared and can be rendered.</returns>
Task<IPublishedRequest> RouteRequestAsync(IPublishedRequestBuilder 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>
Task<bool> TryRouteRequestAsync(IPublishedRequestBuilder request);
/// <param name="options">The options.</param>
/// <returns>The built <see cref="IPublishedRequest"/> instance.</returns>
Task<IPublishedRequest> RouteRequestAsync(IPublishedRequestBuilder request, RouteRequestOptions options);
/// <summary>
/// Updates the request to "not found".

View File

@@ -13,11 +13,9 @@ namespace Umbraco.Web.Routing
/// <summary>
/// Initializes a new instance of the <see cref="PublishedRequest"/> class.
/// </summary>
public PublishedRequest(Uri uri, IPublishedContent publishedContent, bool isInternalRedirect, ITemplate template, DomainAndUri domain, CultureInfo culture, string redirectUrl, int? responseStatusCode, IReadOnlyList<string> cacheExtensions, IReadOnlyDictionary<string, string> headers, bool cacheabilityNoCache)
public PublishedRequest(Uri uri, IPublishedContent publishedContent, bool isInternalRedirect, ITemplate template, DomainAndUri domain, CultureInfo culture, string redirectUrl, int? responseStatusCode, IReadOnlyList<string> cacheExtensions, IReadOnlyDictionary<string, string> headers, bool cacheabilityNoCache, bool ignorePublishedContentCollisions)
{
Uri = uri ?? throw new ArgumentNullException(nameof(uri));
// TODO: What is this?
//IgnorePublishedContentCollisions = ignorePublishedContentCollisions;
PublishedContent = publishedContent;
IsInternalRedirect = isInternalRedirect;
Template = template;
@@ -28,6 +26,7 @@ namespace Umbraco.Web.Routing
CacheExtensions = cacheExtensions;
Headers = headers;
SetNoCacheHeader = cacheabilityNoCache;
IgnorePublishedContentCollisions = ignorePublishedContentCollisions;
}
/// <inheritdoc/>

View File

@@ -18,6 +18,7 @@ namespace Umbraco.Web.Routing
private string _redirectUrl;
private HttpStatusCode? _responseStatus;
private IPublishedContent _publishedContent;
private bool _ignorePublishedContentCollisions;
/// <summary>
/// Initializes a new instance of the <see cref="PublishedRequestBuilder"/> class.
@@ -70,7 +71,8 @@ namespace Umbraco.Web.Routing
_responseStatus.HasValue ? (int?)_responseStatus : null,
_cacheExtensions,
_headers,
_cacheability);
_cacheability,
_ignorePublishedContentCollisions);
/// <inheritdoc/>
public IPublishedRequestBuilder SetNoCacheHeader(bool cacheability)
@@ -190,5 +192,8 @@ namespace Umbraco.Web.Routing
Template = model;
return true;
}
/// <inheritdoc/>
public void IgnorePublishedContentCollisions() => _ignorePublishedContentCollisions = true;
}
}

View File

@@ -100,26 +100,25 @@ namespace Umbraco.Web.Routing
return publishedRequestBuilder;
}
/// <inheritdoc />
public Task<bool> TryRouteRequestAsync(IPublishedRequestBuilder request)
private IPublishedRequest TryRouteRequest(IPublishedRequestBuilder request)
{
FindDomain(request);
// TODO: This was ported from v8 but how could it possibly have a redirect here?
if (request.IsRedirect())
{
return Task.FromResult(false);
return request.Build();
}
// TODO: This was ported from v8 but how could it possibly have content here?
if (request.HasPublishedContent())
{
return Task.FromResult(true);
return request.Build();
}
FindPublishedContent(request);
return Task.FromResult(request.Build().Success());
return request.Build();
}
private void SetVariationContext(CultureInfo culture)
@@ -138,11 +137,18 @@ namespace Umbraco.Web.Routing
}
/// <inheritdoc />
public async Task<IPublishedRequest> RouteRequestAsync(IPublishedRequestBuilder request)
public async Task<IPublishedRequest> RouteRequestAsync(IPublishedRequestBuilder request, RouteRequestOptions options)
{
// outbound routing performs different/simpler logic
if (options.RouteDirection == RouteDirection.Outbound)
{
return TryRouteRequest(request);
}
// find domain
FindDomain(request);
// TODO: This was ported from v8 but how could it possibly have a redirect here?
// if request has been flagged to redirect then return
// whoever called us is in charge of actually redirecting
if (request.IsRedirect())
@@ -156,7 +162,8 @@ namespace Umbraco.Web.Routing
// find the published content if it's not assigned. This could be manually assigned with a custom route handler, or
// with something like EnsurePublishedContentRequestAttribute or UmbracoVirtualNodeRouteHandler. Those in turn call this method
// to setup the rest of the pipeline but we don't want to run the finders since there's one assigned.
if (request.PublishedContent == null)
// TODO: This might very well change when we look into porting custom routes in netcore like EnsurePublishedContentRequestAttribute or UmbracoVirtualNodeRouteHandler.
if (!request.HasPublishedContent())
{
// find the document & template
FindPublishedContentAndTemplate(request);

View File

@@ -0,0 +1,18 @@
namespace Umbraco.Web.Routing
{
/// <summary>
/// The direction of a route
/// </summary>
public enum RouteDirection
{
/// <summary>
/// An inbound route used to map a URL to a content item
/// </summary>
Inbound = 1,
/// <summary>
/// An outbound route used to generate a URL for a content item
/// </summary>
Outbound = 2
}
}

View File

@@ -0,0 +1,29 @@
using System;
namespace Umbraco.Web.Routing
{
/// <summary>
/// Options for routing an Umbraco request
/// </summary>
public struct RouteRequestOptions : IEquatable<RouteRequestOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="RouteRequestOptions"/> struct.
/// </summary>
public RouteRequestOptions(RouteDirection direction) => RouteDirection = direction;
/// <summary>
/// Gets the <see cref="RouteDirection"/>
/// </summary>
public RouteDirection RouteDirection { get; }
/// <inheritdoc/>
public override bool Equals(object obj) => obj is RouteRequestOptions options && Equals(options);
/// <inheritdoc/>
public bool Equals(RouteRequestOptions other) => RouteDirection == other.RouteDirection;
/// <inheritdoc/>
public override int GetHashCode() => 15391035 + RouteDirection.GetHashCode();
}
}

View File

@@ -204,20 +204,19 @@ namespace Umbraco.Web.Routing
}
uri = uriUtility.UriToUmbraco(uri);
IPublishedRequestBuilder pcr = await publishedRouter.CreateRequestAsync(uri);
var routeResult = await publishedRouter.TryRouteRequestAsync(pcr);
IPublishedRequestBuilder builder = await publishedRouter.CreateRequestAsync(uri);
IPublishedRequest pcr = await publishedRouter.RouteRequestAsync(builder, new RouteRequestOptions(RouteDirection.Outbound));
if (pcr.PublishedContent == null)
if (!pcr.HasPublishedContent())
{
var urlInfo = UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture);
return Attempt.Succeed(urlInfo);
}
// TODO: What is this?
//if (pcr.IgnorePublishedContentCollisions)
//{
// return false;
//}
if (pcr.IgnorePublishedContentCollisions)
{
return Attempt<UrlInfo>.Fail();
}
if (pcr.PublishedContent.Id != content.Id)
{

View File

@@ -11,6 +11,7 @@ using Umbraco.Extensions;
using Umbraco.Web.Common.Routing;
using Umbraco.Web.Routing;
using Umbraco.Web.Website.Controllers;
using RouteDirection = Umbraco.Web.Routing.RouteDirection;
namespace Umbraco.Web.Website.Routing
{
@@ -112,7 +113,7 @@ namespace Umbraco.Web.Website.Routing
// an immutable object. The only way to make this better would be to have a RouteRequest
// as part of UmbracoContext but then it will require a PublishedRouter dependency so not sure that's worth it.
// Maybe could be a one-time Set method instead?
IPublishedRequest publishedRequest = umbracoContext.PublishedRequest = await _publishedRouter.RouteRequestAsync(requestBuilder);
IPublishedRequest publishedRequest = umbracoContext.PublishedRequest = await _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(RouteDirection.Inbound));
return publishedRequest;
}

View File

@@ -11,6 +11,7 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.Security;
using Umbraco.Web.Composing;
using Umbraco.Web.Routing;
using RouteDirection = Umbraco.Web.Routing.RouteDirection;
namespace Umbraco.Web
{
@@ -136,7 +137,7 @@ namespace Umbraco.Web
// instantiate, prepare and process the published content request
// important to use CleanedUmbracoUrl - lowercase path-only version of the current URL
var requestBuilder = _publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl).Result;
var request = umbracoContext.PublishedRequest = _publishedRouter.RouteRequestAsync(requestBuilder).Result;
var request = umbracoContext.PublishedRequest = _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(RouteDirection.Inbound)).Result;
// NOTE: This has been ported to netcore
// HandleHttpResponseStatus returns a value indicating that the request should