diff --git a/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs b/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs index 7ce2a54419b0..1d4df53b51ed 100644 --- a/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs +++ b/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs @@ -1,15 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Components.Discovery; using Microsoft.AspNetCore.Components.Endpoints.Infrastructure; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.AspNetCore.StaticAssets.Infrastructure; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Primitives; using static Microsoft.AspNetCore.Internal.LinkerFlags; @@ -184,6 +188,7 @@ private void UpdateEndpoints() private void AddBlazorWebEndpoints(List endpoints) { List blazorWebEndpoints = [ + ..GetBlazorWebJsEndpoint(_endpointRouteBuilder), OpaqueRedirection.GetBlazorOpaqueRedirectionEndpoint()]; foreach (var endpoint in blazorWebEndpoints) @@ -202,6 +207,50 @@ private void AddBlazorWebEndpoints(List endpoints) } } + private static IEnumerable GetBlazorWebJsEndpoint(IEndpointRouteBuilder endpoints) + { + // TODO: Is this how we want to check? + if (StaticAssetsEndpointDataSourceHelper.HasStaticAssetsDataSource(endpoints)) + { + return []; + } + + var app = endpoints.CreateApplicationBuilder(); + + var webHostEnvironment = endpoints.ServiceProvider.GetRequiredService(); + + var options = new StaticFileOptions + { + FileProvider = webHostEnvironment.WebRootFileProvider, + OnPrepareResponse = CacheHeaderSettings.SetCacheHeaders + }; + + app.Use(next => context => + { + // Set endpoint to null so the static files middleware will handle the request. + context.SetEndpoint(null); + + return next(context); + }); + + app.UseStaticFiles(options); + + var requestDelegate = app.Build(); + + var blazorWebJsBuilder = new RouteEndpointBuilder( + requestDelegate, + RoutePatternFactory.Parse("/_framework/blazor.web.js"), + int.MinValue) + { + DisplayName = "Blazor web static files" + }; + + var allowedHttpMethods = new HttpMethodMetadata([HttpMethods.Get, HttpMethods.Head]); + blazorWebJsBuilder.Metadata.Add(allowedHttpMethods); + + return [blazorWebJsBuilder]; + } + public override IChangeToken GetChangeToken() { Initialize(); diff --git a/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs b/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs index dd26550d25f6..a26d2621cd15 100644 --- a/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs +++ b/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs @@ -11,15 +11,18 @@ public sealed class ComponentEndpointConventionBuilder : IHubEndpointConventionB private readonly IEndpointConventionBuilder _hubEndpoint; private readonly IEndpointConventionBuilder _disconnectEndpoint; private readonly IEndpointConventionBuilder _jsInitializersEndpoint; + private readonly IEndpointConventionBuilder? _blazorEndpoint; internal ComponentEndpointConventionBuilder( IEndpointConventionBuilder hubEndpoint, IEndpointConventionBuilder disconnectEndpoint, - IEndpointConventionBuilder jsInitializersEndpoint) + IEndpointConventionBuilder jsInitializersEndpoint, + IEndpointConventionBuilder? blazorEndpoint) { _hubEndpoint = hubEndpoint; _disconnectEndpoint = disconnectEndpoint; _jsInitializersEndpoint = jsInitializersEndpoint; + _blazorEndpoint = blazorEndpoint; } /// @@ -31,6 +34,7 @@ public void Add(Action convention) _hubEndpoint.Add(convention); _disconnectEndpoint.Add(convention); _jsInitializersEndpoint.Add(convention); + _blazorEndpoint?.Add(convention); } /// @@ -39,5 +43,6 @@ public void Finally(Action finalConvention) _hubEndpoint.Finally(finalConvention); _disconnectEndpoint.Finally(finalConvention); _jsInitializersEndpoint.Finally(finalConvention); + _blazorEndpoint?.Finally(finalConvention); } } diff --git a/src/Components/Server/src/Builder/ComponentEndpointRouteBuilderExtensions.cs b/src/Components/Server/src/Builder/ComponentEndpointRouteBuilderExtensions.cs index 26cca2322d0f..8c0f2f97d3cc 100644 --- a/src/Components/Server/src/Builder/ComponentEndpointRouteBuilderExtensions.cs +++ b/src/Components/Server/src/Builder/ComponentEndpointRouteBuilderExtensions.cs @@ -2,9 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components.Server; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.SignalR; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Builder; @@ -85,6 +89,42 @@ public static ComponentEndpointConventionBuilder MapBlazorHub( endpoints.CreateApplicationBuilder().UseMiddleware().Build()) .WithDisplayName("Blazor initializers"); - return new ComponentEndpointConventionBuilder(hubEndpoint, disconnectEndpoint, jsInitializersEndpoint); + var blazorEndpoint = GetBlazorEndpoint(endpoints); + + return new ComponentEndpointConventionBuilder(hubEndpoint, disconnectEndpoint, jsInitializersEndpoint, blazorEndpoint); + } + + private static IEndpointConventionBuilder? GetBlazorEndpoint(IEndpointRouteBuilder endpoints) + { + // TODO: Is this how we want to check? If so, do we want to add the necessary reference to Microsoft.AspNetCore.StaticAssets? + if (false /*StaticAssetsEndpointDataSourceHelper.HasStaticAssetsDataSource(endpoints)*/) + { + return null; + } + + var webHostEnvironment = endpoints.ServiceProvider.GetRequiredService(); + + var options = new StaticFileOptions + { + FileProvider = webHostEnvironment.WebRootFileProvider, + OnPrepareResponse = CacheHeaderSettings.SetCacheHeaders + }; + + var app = endpoints.CreateApplicationBuilder(); + app.Use(next => context => + { + // Set endpoint to null so the static files middleware will handle the request. + context.SetEndpoint(null); + + return next(context); + }); + app.UseStaticFiles(options); + + var blazorEndpoint = endpoints.Map("/_framework/blazor.server.js", app.Build()) + .WithDisplayName("Blazor static files"); + + blazorEndpoint.Add((builder) => ((RouteEndpointBuilder)builder).Order = int.MinValue); + + return blazorEndpoint; } }