diff --git a/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs b/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs
index 4a05521b22..8168d296db 100644
--- a/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs
+++ b/src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs
@@ -36,7 +36,7 @@ internal abstract class RoutingServiceBase
}
protected static string GetContentRoute(DomainAndUri domainAndUri, Uri contentRoute)
- => $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.AbsolutePath)}";
+ => $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.LocalPath)}"; // Use LocalPath over AbsolutePath to keep the path decoded.
protected DomainAndUri? GetDomainAndUriForRoute(Uri contentUrl)
{
diff --git a/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj b/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
index 37816e2f6e..b275c6ad14 100644
--- a/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
+++ b/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
@@ -22,6 +22,9 @@
<_Parameter1>Umbraco.Tests.UnitTests
+
+ <_Parameter1>Umbraco.Tests.Integration
+
<_Parameter1>DynamicProxyGenAssembly2
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/RequestRoutingServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/RequestRoutingServiceTests.cs
new file mode 100644
index 0000000000..3c1586181d
--- /dev/null
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/DeliveryApi/RequestRoutingServiceTests.cs
@@ -0,0 +1,66 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Api.Delivery.Services;
+using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.DeliveryApi;
+using Umbraco.Cms.Core.PublishedCache;
+using Umbraco.Cms.Core.Routing;
+using Umbraco.Cms.Tests.Common.Testing;
+using Umbraco.Cms.Tests.Integration.Testing;
+
+namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi;
+
+[TestFixture]
+[UmbracoTest(
+ Database = UmbracoTestOptions.Database.NewSchemaPerFixture,
+ WithApplication = true)]
+public class RequestRoutingServiceTests : UmbracoIntegrationTest
+{
+ private IRequestRoutingService RequestRoutingService => GetRequiredService();
+
+ protected override void CustomTestSetup(IUmbracoBuilder builder)
+ {
+ builder.Services.AddUnique();
+
+ var elementCache = new FastDictionaryAppCache();
+ var snapshotCache = new FastDictionaryAppCache();
+
+ var domainCacheMock = new Mock();
+ domainCacheMock.Setup(x => x.GetAll(It.IsAny()))
+ .Returns(
+ [
+ new Domain(1, "localhost/en", 1000, "en-us", false, 0),
+ new Domain(2, "localhost/jp", 1000, "ja-jp", false, 1),
+ ]);
+ builder.Services.AddSingleton(provider => domainCacheMock.Object);
+ }
+
+ [TestCase(null, "")]
+ [TestCase("", "")]
+ [TestCase("/", "/")]
+ [TestCase("/en/test/", "1000/test/")] // Verifies matching a domain.
+ [TestCase("/da/test/", "/da/test/")] // Verifies that with no matching domain, so route will be returned as is.
+ [TestCase("/jp/オフィス/", "1000/オフィス/")] // Verifies that with a URL segment containing special characters, the route remains decoded.
+ public void GetContentRoute_ReturnsExpectedRoute(string? requestedRoute, string expectedResult)
+ {
+ if (!string.IsNullOrEmpty(requestedRoute))
+ {
+ var httpContextAccessor = GetRequiredService();
+
+ httpContextAccessor.HttpContext = new DefaultHttpContext
+ {
+ Request =
+ {
+ Scheme = "https",
+ Host = new HostString("localhost"),
+ Path = requestedRoute,
+ },
+ };
+ }
+
+ var result = RequestRoutingService.GetContentRoute(requestedRoute);
+ Assert.AreEqual(expectedResult, result);
+ }
+}