Skip to content

[Blazor] Replace DevServer with BlazorGateway for standalone WASM apps#65982

Merged
javiercn merged 9 commits into
mainfrom
javiercn/blazor-gateway
Apr 20, 2026
Merged

[Blazor] Replace DevServer with BlazorGateway for standalone WASM apps#65982
javiercn merged 9 commits into
mainfrom
javiercn/blazor-gateway

Conversation

@javiercn

@javiercn javiercn commented Mar 25, 2026

Copy link
Copy Markdown
Member

Summary

Introduces Microsoft.AspNetCore.Components.Gateway — a lightweight ASP.NET Core host that replaces the DevServer for serving standalone Blazor WebAssembly applications during development and production.

Changes

New: Gateway project (src/Components/Gateway/src/)

  • BlazorGateway.cs — Static BuildWebHost(string[] args) method configurable in two modes:
    • Standalone mode: Derives static web asset manifest paths from --applicationpath CLI arg (used by dotnet run and E2E tests)
    • Aspire mode: Reads ClientApps config section for multiple client apps, YARP reverse proxy, config endpoints
  • Program.cs — Entry point that calls BlazorGateway.BuildWebHost(args).Run()
  • MSBuild targets (build/Microsoft.AspNetCore.Components.Gateway.targets) — Imported by WASM projects to override dotnet run behavior: sets _WebAssemblyUserRunParameters=true, resolves manifest paths via ComputeRunArguments
  • SpaFallback.targets — Temporary target that adds {**path:nonfile} catch-all endpoint to the static web assets endpoints manifest (clones identity index.html endpoint)

Updated: E2E test infrastructure

  • BlazorWasmTestAppFixture now calls BlazorGateway.BuildWebHost() instead of DevServer
  • Components.TestServer includes BlazorGateway.cs as shared source and uses it for the "Dev server client-side blazor" scenario
  • YARP packages added via the repo's <Reference> pattern (eng/Versions.props + eng/Dependencies.props)
  • SpaFallback.targets imported into BasicTestApp, ThreadingApp, and Wasm.Performance.TestApp

Key design decisions

  • YARP baked in — No conditional compilation; the Gateway always supports reverse proxy for future Aspire integration
  • Shared source via <Compile Include>BlazorGateway.cs is included in test projects as source rather than referenced as a project, avoiding assembly conflicts with the test host's Program class
  • _RemoveTransitiveAspNetCoreFrameworkReference target — In TestServer, removes YARP's transitive FrameworkReference to Microsoft.AspNetCore.App after AddTransitiveFrameworkReferences but before ResolveTargetingPackAssets

Testing

E2E test validation in progress across 5 waves:

  • Wave 1 (smoke): StandaloneAppTest.HasTitle, HasHeading, BasicTestAppCanBeServed
  • Wave 2 (BlazorWasmTestAppFixture): All 20 tests ✅
  • Wave 3-5: Core rendering, events, forms, remaining — in progress

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is temporary until an SDK with the functionality flows here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary until the SDK update flows to asp.net core

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the Blazor WebAssembly DevServer with a new Microsoft.AspNetCore.Components.Gateway host for serving standalone WASM apps in development/testing, and updates test/benchmark assets to use the new Gateway (including SPA fallback routing via static web assets endpoints).

Changes:

  • Adds a new Gateway project (Microsoft.AspNetCore.Components.Gateway) with a BlazorGateway.BuildWebHost(args) entrypoint and dotnet run integration via MSBuild targets.
  • Updates Components test infrastructure (E2E + Components.TestServer) to start the Gateway instead of DevServer and passes static web assets manifests explicitly.
  • Introduces a temporary SpaFallback.targets imported by multiple WASM test apps to add a {**path:nonfile} SPA fallback endpoint.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/WebAssembly/testassets/SpaFallback.targets New MSBuild targets to inject SPA fallback static web asset endpoint.
src/Components/test/testassets/GlobalizationWasmApp/GlobalizationWasmApp.csproj Imports SPA fallback targets for this test asset.
src/Components/test/testassets/Components.TestServer/Program.cs Switches dev-host scenario to use BlazorGateway and passes manifests via args.
src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj Uses shared-source BlazorGateway.cs, adds YARP refs, and removes transitive framework ref.
src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj Imports SPA fallback targets.
src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj Normalizes project file header/encoding.
src/Components/test/E2ETest/Infrastructure/ServerFixtures/BlazorWasmTestAppFixture.cs Uses BlazorGateway and passes static web assets manifests to host.
src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj Imports SPA fallback targets for benchmark app.
src/Components/WebAssembly/testassets/ThreadingApp/ThreadingApp.csproj Imports SPA fallback targets.
src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj Imports Gateway run targets and SPA fallback; sets Gateway DLL location override.
src/Components/WebAssembly/testassets/SpaFallback.targets New MSBuild targets to inject SPA fallback static web asset endpoint (Components copy).
src/Components/Gateway/src/build/Microsoft.AspNetCore.Components.Gateway.targets Overrides dotnet run args for WASM projects to launch Gateway with manifest paths.
src/Components/Gateway/src/blazor-gateway.runtimeconfig.json.in Adds runtimeconfig template for the packaged Gateway tool.
src/Components/Gateway/src/Program.cs Gateway entrypoint calling BlazorGateway.BuildWebHost(args).Run().
src/Components/Gateway/src/Microsoft.AspNetCore.Components.Gateway.nuspec Packages Gateway tool + build targets into the shipping NuGet package.
src/Components/Gateway/src/Microsoft.AspNetCore.Components.Gateway.csproj New shipping project (Web SDK) with YARP/service-discovery references and pack settings.
src/Components/Gateway/src/BlazorGateway.cs Implements Gateway host setup: static web assets, optional YARP proxy, app config endpoints, and static assets mapping per client app.
eng/Versions.props Adds version properties for YARP + service discovery packages.
eng/Dependencies.props Adds YARP + service discovery packages to dependency tracking.

Comment thread src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj Outdated
Comment thread src/Components/Gateway/src/BlazorGateway.cs
Comment thread src/Components/WebAssembly/testassets/SpaFallback.targets
Comment thread src/Components/WebAssembly/testassets/SpaFallback.targets Outdated
Comment thread src/WebAssembly/testassets/SpaFallback.targets Outdated
Comment thread src/Components/test/testassets/GlobalizationWasmApp/GlobalizationWasmApp.csproj Outdated
Comment on lines +59 to +62
// SPA fallback: serve index.html for any non-file URL path, similar to
// the old DevServer's MapFallbackToFile("index.html"). This is registered
// after MapStaticAssets so that specific static file routes take precedence.
app.MapFallbackToFile("index.html");

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is until we get fallbacks on the SDK

@ScarletKuro

ScarletKuro commented Apr 4, 2026

Copy link
Copy Markdown

Does this replaces the old wasm standalone + co-host with ASP.NET core scenario?
If yes, I hope the OverrideHtmlAssetPlaceholders will work out of the box, because with the old one it doesn't dotnet/runtime#121993 (comment) unless you copy client's wwwroot to server's wwwroot
upd: answers were given on Blazor Community Standup

@javiercn javiercn force-pushed the javiercn/blazor-gateway branch 2 times, most recently from 4fe2b05 to b9e014d Compare April 12, 2026 09:07

@ilonatommy ilonatommy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initial pass

Comment thread src/Components/Gateway/src/blazor-gateway.runtimeconfig.json.in Outdated
Comment thread src/Components/Gateway/src/blazor-gateway.runtimeconfig.json.in

@ilonatommy ilonatommy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready for shipping after merge conflicts resolving and one more nit.

Comment thread src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj Outdated
javiercn and others added 6 commits April 16, 2026 10:58
- Single Program.cs with top-level statements, pure config-driven
- Supports standalone (MSBuild targets) and Aspire (env vars/YARP)
- Gateway targets: _WebAssemblyUserRunParameters to skip WasmAppHost,
  AfterTargets ComputeRunArguments with ResolveStaticWebAssetsConfiguration
  to resolve manifest paths via Path.Combine(MSBuildProjectDirectory)
- Temporary SpaFallback.targets in testassets: pure MSBuild target that
  clones identity index.html endpoint as {**path:nonfile} catch-all
  (replaces inline C# task, will be removed when SDK ships
  StaticWebAssetSpaFallbackEnabled)
- Update StandaloneApp.csproj to import Gateway + SpaFallback targets
- Extract BlazorGateway.BuildWebHost() into BlazorGateway.cs (shared source)
- Update Components.TestServer and BlazorWasmTestAppFixture to use BlazorGateway
- Add YARP packages (Yarp.ReverseProxy, ServiceDiscovery) via repo's <Reference> pattern
- Add SpaFallback.targets to BasicTestApp, ThreadingApp, and Wasm.Performance.TestApp
- Remove Program.BuildWebHost.cs Compile Include from E2ETests (gets it via TestServer ref)
Pass explicit --staticWebAssets, --ClientApps:app:EndpointsManifest, and
--ClientApps:app:PathPrefix arguments from call sites instead of deriving
manifest paths inside BlazorGateway. Add SpaFallback.targets to
GlobalizationWasmApp.
SpaFallback.targets added a {**path:nonfile} catch-all to BasicTestApp's endpoint manifest, which propagated to Components.TestServer. MapStaticAssets hardcodes Order=-100 on all endpoints, giving the catch-all higher priority than Razor component routes (Order=0), causing all non-file URLs to serve index.html instead of server-rendered pages.

- Remove SpaFallback.targets import from BasicTestApp.csproj

- Replace the .Add() Order-override in BlazorGateway with MapFallbackToFile, matching the old DevServer approach
- Add Microsoft.Extensions.ServiceDiscovery and
  Microsoft.Extensions.ServiceDiscovery.Yarp to Version.Details.xml
  so Maestro auto-updates them from dotnet/extensions.
- Add version property mappings in Version.Details.props.
- Remove duplicate manual version entries from Versions.props.
- Update ServiceDiscovery packages from 9.2.0 to 10.4.1.
- Yarp.ReverseProxy remains as a manual external dependency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
javiercn and others added 3 commits April 16, 2026 11:04
…ails.props

The Maestro-managed PackageVersion definitions were missing from the
auto-generated section, causing NU1015 (no version specified) errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…gets comment

- Fix tfm in blazor-gateway.runtimeconfig.json.in from net10.0 to net11.0
- Add _CreateRuntimeConfig target to Gateway csproj (port from DevServer)
  so the .in template is processed via GenerateFileFromTemplate
- Add GenerateRuntimeConfigurationFiles=false to use our custom template
- Add XML comment explaining _WebAssemblyUserRunParameters in Gateway.targets

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@javiercn javiercn force-pushed the javiercn/blazor-gateway branch from a3c3df4 to b4aad61 Compare April 16, 2026 09:05
@javiercn javiercn merged commit 2fd33a4 into main Apr 20, 2026
25 checks passed
@javiercn javiercn deleted the javiercn/blazor-gateway branch April 20, 2026 13:47
@dotnet-policy-service dotnet-policy-service Bot added this to the 11.0-preview4 milestone Apr 20, 2026
danroth27 added a commit to danroth27/AspNetCore11Samples that referenced this pull request May 5, 2026
…view

Demonstrates hosting a standalone Blazor WASM app behind the new Blazor
Gateway (PR dotnet/aspnetcore#65982) instead of the legacy Blazor Dev Server:
- ClientApps:app:EndpointsManifest serves WASM static assets
- ReverseProxy section uses YARP to proxy /api/* to the Backend project
- ClientApps:app:ConfigEndpointPath returns inline JSON config to the client
- Service discovery via AddServiceDiscovery + AddServiceDiscoveryDestinationResolver

The local Gateway project mirrors the upstream BlazorGateway.BuildWebHost.
When Microsoft.AspNetCore.Components.Gateway ships, replace it with a single
PackageReference in Client.csproj (in place of the DevServer reference).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danroth27 added a commit to dotnet/core that referenced this pull request May 5, 2026
Documents how to opt in to the new Blazor Gateway (PR dotnet/aspnetcore#65982)
in a standalone Blazor WebAssembly app: swapping the DevServer reference for
Microsoft.AspNetCore.Components.Gateway, configuring YARP proxying to backend
APIs, and exposing a runtime config endpoint to the client. Links to the
GatewayDemo sample in danroth27/AspNetCore11Samples for an end-to-end setup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danroth27 added a commit to danroth27/AspNetCore11Samples that referenced this pull request May 5, 2026
- Drop Microsoft.AspNetCore.Components.WebAssembly.DevServer reference.
- Add a top-level Gateway/ project (local stand-in for the upcoming
  Microsoft.AspNetCore.Components.Gateway package, dotnet/aspnetcore#65982).
- Add a top-level BackendApi/ project (minimal /weather endpoint) so the
  Blazor app can demonstrate the Gateway's YARP proxying support.
- Wire up Weather.razor to call /api/weather (proxied through the Gateway
  to BackendApi) instead of the static sample-data/weather.json.
- Add Pages/AppConfig.razor demonstrating the Gateway's config endpoint:
  the Gateway returns inline JSON from /_app/config (banner text +
  feature flags + environment) and the page renders it.
- Drop the GatewayDemo/ subdirectory it replaces.
- Drop AddServiceDiscovery / AddServiceDiscoveryDestinationResolver from
  the Gateway: these are an Aspire-style integration and aren't needed
  when YARP destinations use plain absolute URLs.
- Replace MapFallbackToFile (requires WebRoot, which the Gateway has
  none of) with a MapFallback that resolves /index.html from the
  static-asset endpoint manifest, so SPA routes return 200.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Blazor] Gateway package for standalone Blazor WebAssembly applications

4 participants