Skip to content

Commit 3586ac9

Browse files
authored
[Static Assets] Improve development experience (#57671)
1 parent 16e32c0 commit 3586ac9

9 files changed

+53
-11
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.StaticAssets;
5+
6+
internal sealed class BuildAssetMetadata { }

src/StaticAssets/src/Development/StaticAssetDevelopmentRuntimeHandler.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
using Microsoft.Extensions.Configuration;
1616
using Microsoft.Extensions.DependencyInjection;
1717
using Microsoft.Extensions.FileProviders;
18-
using Microsoft.Extensions.Hosting;
1918
using Microsoft.Extensions.Logging;
2019
using Microsoft.Extensions.Primitives;
2120
using Microsoft.Net.Http.Headers;
@@ -156,11 +155,11 @@ private static StaticAssetDescriptor FindOriginalAsset(string tag, List<StaticAs
156155
throw new InvalidOperationException("The original asset was not found.");
157156
}
158157

159-
internal static bool IsEnabled(IServiceProvider serviceProvider, IWebHostEnvironment environment)
158+
internal static bool IsEnabled(bool isBuildManifest, IServiceProvider serviceProvider)
160159
{
161160
var config = serviceProvider.GetRequiredService<IConfiguration>();
162161
var explicitlyConfigured = bool.TryParse(config[ReloadStaticAssetsAtRuntimeKey], out var hotReload);
163-
return (!explicitlyConfigured && environment.IsDevelopment()) || (explicitlyConfigured && hotReload);
162+
return (!explicitlyConfigured && isBuildManifest) || (explicitlyConfigured && hotReload);
164163
}
165164

166165
internal static void EnableSupport(

src/StaticAssets/src/LoggerExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,10 @@ public static void FileServed(this ILogger logger, string virtualPath, string ph
5959
[LoggerMessage(16, LogLevel.Warning,
6060
"The WebRootPath was not found: {WebRootPath}. Static files may be unavailable.", EventName = "WebRootPathNotFound")]
6161
public static partial void WebRootPathNotFound(this ILogger logger, string webRootPath);
62+
63+
[LoggerMessage(17, LogLevel.Warning,
64+
"The application is not running against the published output and Static Web Assets are not enabled." +
65+
" To configure static web assets in other environments, call 'StaticWebAssetsLoader.UseStaticWebAssets(IWebHostEnvironment, IConfiguration)' to enable them.", EventName = "StaticWebAssetsNotEnabled")]
66+
public static partial void EnsureStaticWebAssetsEnabled(this ILogger logger);
6267
}
6368

src/StaticAssets/src/StaticAssetEndpointDataSource.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ internal StaticAssetsEndpointDataSource(
2828
IServiceProvider serviceProvider,
2929
StaticAssetEndpointFactory endpointFactory,
3030
string manifestName,
31+
bool isBuildManifest,
3132
List<StaticAssetDescriptor> descriptors)
3233
{
3334
ServiceProvider = serviceProvider;
@@ -37,8 +38,14 @@ internal StaticAssetsEndpointDataSource(
3738
_cancellationTokenSource = new CancellationTokenSource();
3839
_changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);
3940

41+
if (isBuildManifest)
42+
{
43+
_conventions.Add(c => c.Metadata.Add(new BuildAssetMetadata()));
44+
}
45+
4046
DefaultBuilder = new StaticAssetsEndpointConventionBuilder(
4147
_lock,
48+
isBuildManifest,
4249
descriptors,
4350
_conventions,
4451
_finallyConventions);

src/StaticAssets/src/StaticAssetsEndpointConventionBuilder.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,24 @@ namespace Microsoft.AspNetCore.StaticAssets;
1111
public sealed class StaticAssetsEndpointConventionBuilder : IEndpointConventionBuilder
1212
{
1313
private readonly object _lck;
14+
private readonly bool _isBuildManifest;
1415
private readonly List<StaticAssetDescriptor> _descriptors;
1516
private readonly List<Action<EndpointBuilder>> _conventions;
1617
private readonly List<Action<EndpointBuilder>> _finallyConventions;
1718

18-
internal StaticAssetsEndpointConventionBuilder(object lck, List<StaticAssetDescriptor> descriptors, List<Action<EndpointBuilder>> conventions, List<Action<EndpointBuilder>> finallyConventions)
19+
internal StaticAssetsEndpointConventionBuilder(object lck, bool isBuildManifest, List<StaticAssetDescriptor> descriptors, List<Action<EndpointBuilder>> conventions, List<Action<EndpointBuilder>> finallyConventions)
1920
{
2021
_lck = lck;
22+
_isBuildManifest = isBuildManifest;
2123
_descriptors = descriptors;
2224
_conventions = conventions;
2325
_finallyConventions = finallyConventions;
2426
}
2527

2628
internal List<StaticAssetDescriptor> Descriptors => _descriptors;
2729

30+
internal bool IsBuildManifest => _isBuildManifest;
31+
2832
/// <inheritdoc/>
2933
public void Add(Action<EndpointBuilder> convention)
3034
{

src/StaticAssets/src/StaticAssetsEndpointRouteBuilderExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static StaticAssetsEndpointConventionBuilder MapStaticAssets(this IEndpoi
3636

3737
var result = MapStaticAssetsCore(endpoints, staticAssetsManifestPath);
3838

39-
if (StaticAssetDevelopmentRuntimeHandler.IsEnabled(endpoints.ServiceProvider, environment))
39+
if (StaticAssetDevelopmentRuntimeHandler.IsEnabled(result.IsBuildManifest, endpoints.ServiceProvider))
4040
{
4141
StaticAssetDevelopmentRuntimeHandler.EnableSupport(endpoints, result, environment, result.Descriptors);
4242
}
@@ -56,7 +56,7 @@ private static StaticAssetsEndpointConventionBuilder MapStaticAssetsCore(
5656

5757
var manifest = ResolveManifest(manifestPath);
5858

59-
var dataSource = StaticAssetsManifest.CreateDataSource(endpoints, manifestPath, manifest.Endpoints);
59+
var dataSource = StaticAssetsManifest.CreateDataSource(endpoints, manifestPath, manifest.Endpoints, manifest.IsBuildManifest());
6060
return dataSource.DefaultBuilder;
6161
}
6262

@@ -89,9 +89,9 @@ internal static StaticAssetsEndpointConventionBuilder MapStaticAssets(this IEndp
8989
ArgumentNullException.ThrowIfNull(endpoints);
9090

9191
var environment = endpoints.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
92-
var result = StaticAssetsManifest.CreateDataSource(endpoints, "", manifest.Endpoints).DefaultBuilder;
92+
var result = StaticAssetsManifest.CreateDataSource(endpoints, "", manifest.Endpoints, manifest.IsBuildManifest()).DefaultBuilder;
9393

94-
if (StaticAssetDevelopmentRuntimeHandler.IsEnabled(endpoints.ServiceProvider, environment))
94+
if (StaticAssetDevelopmentRuntimeHandler.IsEnabled(result.IsBuildManifest, endpoints.ServiceProvider))
9595
{
9696
StaticAssetDevelopmentRuntimeHandler.EnableSupport(endpoints, result, environment, result.Descriptors);
9797
}

src/StaticAssets/src/StaticAssetsInvoker.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33

44
using System.Diagnostics;
55
using System.Globalization;
6+
using Microsoft.AspNetCore.Hosting;
67
using Microsoft.AspNetCore.Http;
78
using Microsoft.AspNetCore.Http.Headers;
89
using Microsoft.AspNetCore.Internal;
10+
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.FileProviders;
12+
using Microsoft.Extensions.Hosting;
1013
using Microsoft.Extensions.Logging;
1114
using Microsoft.Net.Http.Headers;
1215

@@ -81,7 +84,7 @@ public StaticAssetsInvoker(StaticAssetDescriptor resource, IFileProvider filePro
8184
public IFileInfo FileInfo => _fileInfo ??=
8285
_fileProvider.GetFileInfo(_resource.AssetPath) is IFileInfo file and { Exists: true } ?
8386
file :
84-
throw new InvalidOperationException($"The file '{_resource.AssetPath}' could not be found.");
87+
throw new FileNotFoundException($"The file '{_resource.AssetPath}' could not be found.");
8588

8689
private Task ApplyResponseHeadersAsync(StaticAssetInvocationContext context, int statusCode)
8790
{
@@ -157,6 +160,14 @@ public async Task Invoke(HttpContext context)
157160
}
158161
catch (FileNotFoundException)
159162
{
163+
if (context.GetEndpoint() is Endpoint { Metadata: { } metadata } && metadata.GetMetadata<BuildAssetMetadata>() != null)
164+
{
165+
var environment = context.RequestServices.GetRequiredService<IWebHostEnvironment>();
166+
if (!environment.IsDevelopment() && environment.WebRootFileProvider is not CompositeFileProvider)
167+
{
168+
_logger.EnsureStaticWebAssetsEnabled();
169+
}
170+
}
160171
context.Response.Clear();
161172
}
162173
return;

src/StaticAssets/src/StaticAssetsManifest.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,23 @@ internal static StaticAssetsManifest Parse(string manifestPath)
3535
return result;
3636
}
3737

38-
internal static StaticAssetsEndpointDataSource CreateDataSource(IEndpointRouteBuilder endpoints, string manifestName, List<StaticAssetDescriptor> descriptors)
38+
internal static StaticAssetsEndpointDataSource CreateDataSource(IEndpointRouteBuilder endpoints, string manifestName, List<StaticAssetDescriptor> descriptors, bool isBuildManifest)
3939
{
40-
var dataSource = new StaticAssetsEndpointDataSource(endpoints.ServiceProvider, new StaticAssetEndpointFactory(endpoints.ServiceProvider), manifestName, descriptors);
40+
var dataSource = new StaticAssetsEndpointDataSource(
41+
endpoints.ServiceProvider,
42+
new StaticAssetEndpointFactory(endpoints.ServiceProvider),
43+
manifestName,
44+
isBuildManifest,
45+
descriptors);
4146
endpoints.DataSources.Add(dataSource);
4247
return dataSource;
4348
}
4449

4550
public int Version { get; set; }
4651

52+
public string ManifestType { get; set; } = "";
53+
4754
public List<StaticAssetDescriptor> Endpoints { get; set; } = [];
55+
56+
public bool IsBuildManifest() => string.Equals(ManifestType, "Build", StringComparison.OrdinalIgnoreCase);
4857
}

src/StaticAssets/test/StaticAssetsIntegrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ private static void CreateTestManifest(string appName, string webRoot, params Sp
492492
var lastModified = DateTimeOffset.UtcNow;
493493
File.WriteAllText(filePath, resource.Content);
494494
var hash = GetEtag(resource.Content);
495+
manifest.ManifestType = "Build";
495496
manifest.Endpoints.Add(new StaticAssetDescriptor
496497
{
497498
Route = resource.Path,

0 commit comments

Comments
 (0)