Skip to content

Commit c6effef

Browse files
AndyButlandkjac
andauthored
Handle file paths as not found in delivery API by route requests (#19063)
* Handle file paths as not found in delivery API by route requests. * Move check earlier to handle redirect logic as well. * Spelling: Changed "resolveable" to "resolvable" --------- Co-authored-by: kjac <[email protected]>
1 parent 7d9a2c6 commit c6effef

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ private async Task<IActionResult> HandleRequest(string path)
145145
path = DecodePath(path);
146146
path = path.Length == 0 ? "/" : path;
147147

148+
if (_apiContentPathResolver.IsResolvablePath(path) is false)
149+
{
150+
return NotFound();
151+
}
152+
148153
IPublishedContent? contentItem = GetContent(path);
149154
if (contentItem is not null)
150155
{

src/Umbraco.Core/DeliveryApi/ApiContentPathResolver.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ public ApiContentPathResolver(IRequestRoutingService requestRoutingService, IApi
1515
_apiPublishedContentCache = apiPublishedContentCache;
1616
}
1717

18+
public virtual bool IsResolvablePath(string path)
19+
{
20+
// File requests will blow up with an downstream exception in GetRequiredPublishedSnapshot, which fails due to an UmbracoContext
21+
// not being available for what's considered a static file request.
22+
// See: https://github.com/umbraco/Umbraco-CMS/issues/19051
23+
// Given a URL segment and hence route can't contain a period, we can safely assume that if the last segment of the path contains
24+
// a period, it's a file request and should return null here.
25+
if (IsFileRequest(path))
26+
{
27+
return false;
28+
}
29+
30+
return true;
31+
}
32+
33+
private static bool IsFileRequest(string path) => path.Split('/', StringSplitOptions.RemoveEmptyEntries).Last().Contains('.');
34+
1835
public virtual IPublishedContent? ResolveContentPath(string path)
1936
{
2037
path = path.EnsureStartsWith("/");

src/Umbraco.Core/DeliveryApi/IApiContentPathResolver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ namespace Umbraco.Cms.Core.DeliveryApi;
44

55
public interface IApiContentPathResolver
66
{
7+
bool IsResolvablePath(string path) => true;
8+
79
IPublishedContent? ResolveContentPath(string path);
810
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using Moq;
2+
using NUnit.Framework;
3+
using Umbraco.Cms.Core.DeliveryApi;
4+
using Umbraco.Cms.Core.Models.PublishedContent;
5+
6+
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi;
7+
8+
[TestFixture]
9+
public class ApiContentPathResolverTests
10+
{
11+
private const string TestPath = "/test/page";
12+
13+
[TestCase(TestPath, true)]
14+
[TestCase("file.txt", false)]
15+
[TestCase("test/file.txt", false)]
16+
[TestCase("test/test2/file.txt", false)]
17+
[TestCase("/file.txt", false)]
18+
[TestCase("/test/file.txt", false)]
19+
[TestCase("/test/test2/file.txt", false)]
20+
public void Can_Verify_Resolveable_Paths(string path, bool expected)
21+
{
22+
var resolver = CreateResolver();
23+
var result = resolver.IsResolvablePath(path);
24+
Assert.AreEqual(expected, result);
25+
}
26+
27+
[Test]
28+
public void Resolves_Content_For_Path()
29+
{
30+
var resolver = CreateResolver();
31+
var result = resolver.ResolveContentPath(TestPath);
32+
Assert.IsNotNull(result);
33+
}
34+
35+
private static ApiContentPathResolver CreateResolver()
36+
{
37+
var mockRequestRoutingService = new Mock<IRequestRoutingService>();
38+
mockRequestRoutingService
39+
.Setup(x => x.GetContentRoute(It.IsAny<string>()))
40+
.Returns((string path) => path);
41+
var mockApiPublishedContentCache = new Mock<IApiPublishedContentCache>();
42+
mockApiPublishedContentCache
43+
.Setup(x => x.GetByRoute(It.Is<string>(y => y == TestPath)))
44+
.Returns(new Mock<IPublishedContent>().Object);
45+
return new ApiContentPathResolver(mockRequestRoutingService.Object, mockApiPublishedContentCache.Object);
46+
}
47+
}

0 commit comments

Comments
 (0)