Delivery API: Not Found response when the start-item in "Start-Item" header is invalid (#14217)
* Update comment * Implement ValidateStartItemAttribute * Implement StartItemHeaderHasValue() on IRequestStartItemProvider * Fix case-sensitivity problem on url segment * Change StartItemHeaderHasValue() to RequestedStartItem() * Fix variant start-item header bug * Rearranging attributes * Revert to not explicitly get the culture from IRequestCultureService --------- Co-authored-by: kjac <kja@umbraco.dk>
This commit is contained in:
committed by
GitHub
parent
6f3cf6ead5
commit
7ed59f05cb
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Delivery.Filters;
|
||||
using Umbraco.Cms.Api.Delivery.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
@@ -9,6 +10,8 @@ namespace Umbraco.Cms.Api.Delivery.Controllers;
|
||||
|
||||
[VersionedDeliveryApiRoute("content")]
|
||||
[ApiExplorerSettings(GroupName = "Content")]
|
||||
[LocalizeFromAcceptLanguageHeader]
|
||||
[ValidateStartItem]
|
||||
public abstract class ContentApiControllerBase : DeliveryApiControllerBase
|
||||
{
|
||||
protected IApiPublishedContentCache ApiPublishedContentCache { get; }
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace Umbraco.Cms.Api.Delivery.Controllers;
|
||||
[ApiVersion("1.0")]
|
||||
[DeliveryApiAccess]
|
||||
[JsonOptionsName(Constants.JsonOptionsNames.DeliveryApi)]
|
||||
[LocalizeFromAcceptLanguageHeader]
|
||||
public abstract class DeliveryApiControllerBase : Controller
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class QueryContentApiController : ContentApiControllerBase
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<IApiContentResponse>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Query(
|
||||
string? fetch,
|
||||
[FromQuery] string[] filter,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Filters;
|
||||
|
||||
internal sealed class ValidateStartItemAttribute : TypeFilterAttribute
|
||||
{
|
||||
public ValidateStartItemAttribute()
|
||||
: base(typeof(ValidateStartItemFilter))
|
||||
{
|
||||
}
|
||||
|
||||
private class ValidateStartItemFilter : IActionFilter
|
||||
{
|
||||
private readonly IRequestStartItemProvider _requestStartItemProvider;
|
||||
|
||||
public ValidateStartItemFilter(IRequestStartItemProvider requestStartItemProvider)
|
||||
=> _requestStartItemProvider = requestStartItemProvider;
|
||||
|
||||
public void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (_requestStartItemProvider.RequestedStartItem() is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IPublishedContent? startItem = _requestStartItemProvider.GetStartItem();
|
||||
|
||||
if (startItem is null)
|
||||
{
|
||||
context.Result = new NotFoundObjectResult("The Start-Item could not be found");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ public sealed class ContentTypeFilter : IFilterHandler
|
||||
Value = string.Empty
|
||||
};
|
||||
|
||||
// TODO: do we support negation?
|
||||
// Support negation
|
||||
if (alias.StartsWith('!'))
|
||||
{
|
||||
filterOption.Value = alias.Substring(1);
|
||||
|
||||
@@ -22,7 +22,7 @@ public sealed class NameFilter : IFilterHandler
|
||||
Value = string.Empty
|
||||
};
|
||||
|
||||
// TODO: do we support negation?
|
||||
// Support negation
|
||||
if (value.StartsWith('!'))
|
||||
{
|
||||
filterOption.Value = value.Substring(1);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
@@ -9,15 +10,20 @@ namespace Umbraco.Cms.Api.Delivery.Services;
|
||||
internal sealed class RequestStartItemProvider : RequestHeaderHandler, IRequestStartItemProvider
|
||||
{
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
|
||||
// this provider lifetime is Scope, so we can cache this as a field
|
||||
private IPublishedContent? _requestedStartContent;
|
||||
|
||||
public RequestStartItemProvider(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor)
|
||||
: base(httpContextAccessor) =>
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
: base(httpContextAccessor)
|
||||
{
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IPublishedContent? GetStartItem()
|
||||
@@ -27,13 +33,14 @@ internal sealed class RequestStartItemProvider : RequestHeaderHandler, IRequestS
|
||||
return _requestedStartContent;
|
||||
}
|
||||
|
||||
var headerValue = GetHeaderValue("Start-Item");
|
||||
var headerValue = RequestedStartItem()?.Trim(Constants.CharArrays.ForwardSlash);
|
||||
if (headerValue.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot) == false || publishedSnapshot?.Content == null)
|
||||
if (_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot) == false ||
|
||||
publishedSnapshot?.Content == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -42,8 +49,11 @@ internal sealed class RequestStartItemProvider : RequestHeaderHandler, IRequestS
|
||||
|
||||
_requestedStartContent = Guid.TryParse(headerValue, out Guid key)
|
||||
? rootContent.FirstOrDefault(c => c.Key == key)
|
||||
: rootContent.FirstOrDefault(c => c.UrlSegment == headerValue);
|
||||
: rootContent.FirstOrDefault(c => c.UrlSegment(_variationContextAccessor).InvariantEquals(headerValue));
|
||||
|
||||
return _requestedStartContent;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? RequestedStartItem() => GetHeaderValue("Start-Item");
|
||||
}
|
||||
|
||||
@@ -5,7 +5,12 @@ namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
public interface IRequestStartItemProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the requested start item from the "Start-Item" header, if present.
|
||||
/// Gets the requested start item, if present.
|
||||
/// </summary>
|
||||
IPublishedContent? GetStartItem();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the requested start item, if present.
|
||||
/// </summary>
|
||||
string? RequestedStartItem();
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@ internal sealed class NoopRequestStartItemProvider : IRequestStartItemProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IPublishedContent? GetStartItem() => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? RequestedStartItem() => null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user