Browse Source

Merge pull request #25337 from abpframework/auto-merge/rel-10-2/4528

Merge branch rel-10.3 with rel-10.2
pull/25338/head
Volosoft Agent 5 months ago
committed by GitHub
parent
commit
f36f133152
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      framework/Volo.Abp.slnx
  2. 23
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpScripts.razor
  3. 23
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpStyles.razor
  4. 57
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/ComponentBundleUrlBuilder.cs
  5. 8
      framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/IComponentBundleUrlBuilder.cs
  6. 3
      framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.Tests.abppkg
  7. 17
      framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.Tests.csproj
  8. 105
      framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo/Abp/AspNetCore/Components/Web/Theming/Bundling/ComponentBundleUrlBuilder_Tests.cs

1
framework/Volo.Abp.slnx

@ -180,6 +180,7 @@
<Project Path="test/Volo.Abp.AspNetCore.Mvc.PlugIn/Volo.Abp.AspNetCore.Mvc.PlugIn.csproj" /> <Project Path="test/Volo.Abp.AspNetCore.Mvc.PlugIn/Volo.Abp.AspNetCore.Mvc.PlugIn.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj" /> <Project Path="test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj" /> <Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj" /> <Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj" /> <Project Path="test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo.Abp.AspNetCore.Mvc.Client.Tests.csproj" /> <Project Path="test/Volo.Abp.AspNetCore.Mvc.Client.Tests/Volo.Abp.AspNetCore.Mvc.Client.Tests.csproj" />

23
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpScripts.razor

@ -1,16 +1,14 @@
@using Microsoft.AspNetCore.Components
@implements IDisposable @implements IDisposable
@inject IComponentBundleManager BundleManager @inject IComponentBundleManager BundleManager
@inject PersistentComponentState ApplicationState @inject PersistentComponentState ApplicationState
@inject NavigationManager NavigationManager
@inject IComponentBundleUrlBuilder BundleUrlBuilder
@if (ScriptFiles != null) @if (ScriptFiles != null)
{ {
foreach (var file in ScriptFiles) foreach (var file in ScriptFiles)
{ {
var src = file; <script src="@file"></script>
if (!AppBasePath.IsNullOrWhiteSpace())
{
src = AppBasePath.EnsureEndsWith('/') + file.RemovePreFix("/");
}
<script src="@src"></script>
} }
} }
@ -39,7 +37,8 @@
// We are in prerendering mode // We are in prerendering mode
if (!BundleName.IsNullOrWhiteSpace()) if (!BundleName.IsNullOrWhiteSpace())
{ {
ScriptFiles = (await BundleManager.GetScriptBundleFilesAsync(BundleName!)).ToList(); var rawFiles = await BundleManager.GetScriptBundleFilesAsync(BundleName!);
ScriptFiles = await ResolveAsync(rawFiles);
} }
} }
else else
@ -51,6 +50,16 @@
} }
} }
private async Task<List<string>> ResolveAsync(IReadOnlyList<string> files)
{
var resolved = new List<string>(files.Count);
foreach (var file in files)
{
resolved.Add(await BundleUrlBuilder.BuildAsync(file, AppBasePath, NavigationManager.BaseUri));
}
return resolved;
}
private Task Callback() private Task Callback()
{ {
ApplicationState.PersistAsJson(PrerenderedKey, PrerenderedKey); ApplicationState.PersistAsJson(PrerenderedKey, PrerenderedKey);

23
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/AbpStyles.razor

@ -1,16 +1,14 @@
@using Microsoft.AspNetCore.Components
@implements IDisposable @implements IDisposable
@inject IComponentBundleManager BundleManager @inject IComponentBundleManager BundleManager
@inject PersistentComponentState ApplicationState @inject PersistentComponentState ApplicationState
@inject NavigationManager NavigationManager
@inject IComponentBundleUrlBuilder BundleUrlBuilder
@if (StyleFiles != null) @if (StyleFiles != null)
{ {
foreach (var file in StyleFiles) foreach (var file in StyleFiles)
{ {
var href = file; <link rel="stylesheet" href="@file" />
if (!AppBasePath.IsNullOrWhiteSpace())
{
href = AppBasePath.EnsureEndsWith('/') + file.RemovePreFix("/");
}
<link rel="stylesheet" href="@href" />
} }
} }
@ -39,7 +37,8 @@
// We are in prerendering mode // We are in prerendering mode
if (!BundleName.IsNullOrWhiteSpace()) if (!BundleName.IsNullOrWhiteSpace())
{ {
StyleFiles = (await BundleManager.GetStyleBundleFilesAsync(BundleName!)).ToList(); var rawFiles = await BundleManager.GetStyleBundleFilesAsync(BundleName!);
StyleFiles = await ResolveAsync(rawFiles);
} }
} }
else else
@ -65,6 +64,16 @@
} }
} }
private async Task<List<string>> ResolveAsync(IReadOnlyList<string> files)
{
var resolved = new List<string>(files.Count);
foreach (var file in files)
{
resolved.Add(await BundleUrlBuilder.BuildAsync(file, AppBasePath, NavigationManager.BaseUri));
}
return resolved;
}
private Task Callback() private Task Callback()
{ {
ApplicationState.PersistAsJson(PrerenderedKey, StyleFiles); ApplicationState.PersistAsJson(PrerenderedKey, StyleFiles);

57
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/ComponentBundleUrlBuilder.cs

@ -0,0 +1,57 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Bundling;
public class ComponentBundleUrlBuilder : IComponentBundleUrlBuilder, ITransientDependency
{
public virtual Task<string> BuildAsync(
[NotNull] string fileName,
[CanBeNull] string? appBasePath,
[CanBeNull] string? navigationBaseUri)
{
Check.NotNull(fileName, nameof(fileName));
if (IsExternalUrl(fileName))
{
return Task.FromResult(fileName);
}
var pathBase = !string.IsNullOrWhiteSpace(appBasePath)
? appBasePath
: ExtractPathBaseFromNavigationBaseUri(navigationBaseUri);
if (string.IsNullOrWhiteSpace(pathBase))
{
return Task.FromResult(fileName);
}
var normalized = pathBase.EnsureEndsWith('/');
if (normalized == "/")
{
return Task.FromResult(fileName);
}
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.IsNullOrWhiteSpace(navigationBaseUri))
{
return null;
}
return Uri.TryCreate(navigationBaseUri, UriKind.Absolute, out var uri)
? uri.AbsolutePath
: null;
}
}

8
framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Bundling/IComponentBundleUrlBuilder.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Bundling;
public interface IComponentBundleUrlBuilder
{
Task<string> BuildAsync(string fileName, string? appBasePath, string? navigationBaseUri);
}

3
framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.Tests.abppkg

@ -0,0 +1,3 @@
{
"role": "lib.test"
}

17
framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.Tests.csproj

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace />
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
</Project>

105
framework/test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo/Abp/AspNetCore/Components/Web/Theming/Bundling/ComponentBundleUrlBuilder_Tests.cs

@ -0,0 +1,105 @@
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Bundling;
public class ComponentBundleUrlBuilder_Tests
{
private readonly IComponentBundleUrlBuilder _builder = new ComponentBundleUrlBuilder();
[Fact]
public async Task Should_Return_FileName_When_No_PathBase_Available()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: null, navigationBaseUri: null))
.ShouldBe("/__bundles/Global.css");
}
[Fact]
public async Task Should_Return_FileName_When_NavigationBaseUri_Has_Root_PathBase()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: null, navigationBaseUri: "https://localhost/"))
.ShouldBe("/__bundles/Global.css");
}
[Fact]
public async Task Should_Use_Explicit_AppBasePath_Over_NavigationBaseUri()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: "/explicit", navigationBaseUri: "https://localhost/from-nav/"))
.ShouldBe("/explicit/__bundles/Global.css");
}
[Fact]
public async Task Should_Resolve_PathBase_From_NavigationBaseUri()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: null, navigationBaseUri: "https://localhost/foo/"))
.ShouldBe("/foo/__bundles/Global.css");
}
[Fact]
public async Task Should_Resolve_PathBase_From_NavigationBaseUri_When_AppBasePath_Empty()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: "", navigationBaseUri: "https://localhost/foo/"))
.ShouldBe("/foo/__bundles/Global.css");
}
[Fact]
public async Task Should_Handle_Nested_PathBase()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: null, navigationBaseUri: "https://localhost/foo/bar/"))
.ShouldBe("/foo/bar/__bundles/Global.css");
}
[Fact]
public async Task Should_Normalize_AppBasePath_Without_Trailing_Slash()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: "/foo", navigationBaseUri: null))
.ShouldBe("/foo/__bundles/Global.css");
}
[Fact]
public async Task Should_Not_Duplicate_Leading_Slash()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: "/foo/", navigationBaseUri: null))
.ShouldBe("/foo/__bundles/Global.css");
}
[Fact]
public async Task Should_Handle_FileName_Without_Leading_Slash()
{
(await _builder.BuildAsync("__bundles/Global.css", appBasePath: "/foo", navigationBaseUri: null))
.ShouldBe("/foo/__bundles/Global.css");
}
[Fact]
public async Task Should_Return_FileName_When_NavigationBaseUri_Is_Invalid()
{
(await _builder.BuildAsync("/__bundles/Global.css", appBasePath: null, navigationBaseUri: "not-a-uri"))
.ShouldBe("/__bundles/Global.css");
}
[Fact]
public async Task Should_Throw_When_FileName_Is_Null()
{
await Should.ThrowAsync<System.ArgumentNullException>(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");
}
}
Loading…
Cancel
Save