diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/ComponentBundleUrlBuilder.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/ComponentBundleUrlBuilder.cs index 28304aba83..7d27f1711c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/ComponentBundleUrlBuilder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/ComponentBundleUrlBuilder.cs @@ -14,11 +14,16 @@ public class ComponentBundleUrlBuilder : IComponentBundleUrlBuilder, ITransientD { Check.NotNull(fileName, nameof(fileName)); - var pathBase = !string.IsNullOrEmpty(appBasePath) + if (IsExternalUrl(fileName)) + { + return Task.FromResult(fileName); + } + + var pathBase = !string.IsNullOrWhiteSpace(appBasePath) ? appBasePath : ExtractPathBaseFromNavigationBaseUri(navigationBaseUri); - if (string.IsNullOrEmpty(pathBase)) + if (string.IsNullOrWhiteSpace(pathBase)) { return Task.FromResult(fileName); } @@ -32,9 +37,15 @@ public class ComponentBundleUrlBuilder : IComponentBundleUrlBuilder, ITransientD return Task.FromResult(normalized + fileName.RemovePreFix("/")); } + protected virtual bool IsExternalUrl([NotNull] string fileName) + { + return fileName.StartsWith("//", StringComparison.Ordinal) || + (fileName.Contains(':') && Uri.TryCreate(fileName, UriKind.Absolute, out _)); + } + protected virtual string? ExtractPathBaseFromNavigationBaseUri([CanBeNull] string? navigationBaseUri) { - if (string.IsNullOrEmpty(navigationBaseUri)) + if (string.IsNullOrWhiteSpace(navigationBaseUri)) { return null; } diff --git a/framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo/Abp/AspNetCore/Components/Web/Theming/Bundling/ComponentBundleUrlBuilder_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo/Abp/AspNetCore/Components/Web/Theming/Bundling/ComponentBundleUrlBuilder_Tests.cs index 58b60b230d..a47250aaa4 100644 --- a/framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo/Abp/AspNetCore/Components/Web/Theming/Bundling/ComponentBundleUrlBuilder_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo/Abp/AspNetCore/Components/Web/Theming/Bundling/ComponentBundleUrlBuilder_Tests.cs @@ -84,4 +84,22 @@ public class ComponentBundleUrlBuilder_Tests await Should.ThrowAsync(async () => await _builder.BuildAsync(null!, appBasePath: "/foo", navigationBaseUri: null)); } + + [Theory] + [InlineData("https://cdn.example.com/foo.css")] + [InlineData("http://cdn.example.com/foo.css")] + [InlineData("//cdn.example.com/foo.css")] + [InlineData("data:text/css;base64,Zm9vIA==")] + public async Task Should_Not_Prefix_External_Urls(string externalUrl) + { + (await _builder.BuildAsync(externalUrl, appBasePath: "/foo", navigationBaseUri: "https://localhost/foo/")) + .ShouldBe(externalUrl); + } + + [Fact] + public async Task Should_Treat_Whitespace_AppBasePath_As_Not_Provided() + { + (await _builder.BuildAsync("/__bundles/Global.css", appBasePath: " ", navigationBaseUri: "https://localhost/foo/")) + .ShouldBe("/foo/__bundles/Global.css"); + } }