Serve module static web assets on the minimal-API host - #44
Conversation
Resolve the follow-up left by #43: a module hosted via UseAspNetCoreMinimal now serves its own static web assets (wwwroot / _content), on net6/8/9/10, in both dev (dotnet run) and published output. Each module's inner WebApplication has ApplicationName = the module assembly, so the module's own static web assets manifest is already root-mapped. After Build() the handler composes those assets onto the module's WebRootFileProvider via StaticWebAssetsLoader.UseStaticWebAssets (dev) and ModuleWebAssetsLoader.UseModuleAssets (published) - no ".modules/{module}" prefix stripping, and each module host serves only its own assets. Assets are served through the classic UseStaticFiles pipeline (WebRootFileProvider resolved lazily by the middleware after build). Verified against samples/dotnet/minimal: GET /css/site.css -> 200 in dev and publish.
There was a problem hiding this comment.
Pull request overview
Enables Hosuto web modules hosted via UseAspNetCoreMinimal() to serve their own static web assets (wwwroot / _content) from the module’s inner WebApplication, aligning minimal-host behavior with the classic host across dev (dotnet run) and publish outputs.
Changes:
- Load and compose module static web assets onto the inner host’s
WebRootFileProviderafterWebApplication.Build(). - Update the minimal sample README to reflect that module static assets now resolve (200) in both dev and published output.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Hosuto.Hosting.AspNetCore/Modules/Hosting/WebApplicationModuleHostHandler.cs | Adds post-build static web asset composition for minimal-hosted modules. |
| samples/dotnet/minimal/README.md | Updates sample documentation to describe the now-working static asset behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private static void MapModuleStaticWebAssets(IServiceProvider hostServices) | ||
| { | ||
| var environment = hostServices.GetService<IWebHostEnvironment>(); | ||
| var configuration = hostServices.GetService<IConfiguration>(); | ||
| if (environment == null || configuration == null) | ||
| return; | ||
|
|
||
| // dev: reads {module}.staticwebassets.runtime.json (assets mapped at the web root). | ||
| StaticWebAssetsLoader.UseStaticWebAssets(environment, configuration); | ||
| // published: composes {contentRoot}/wwwroot/.modules/{module} physically. | ||
| ModuleWebAssets.ModuleWebAssetsLoader.UseModuleAssets(environment, configuration); | ||
| } |
| command.Host = builder.Build(); | ||
| MapModuleStaticWebAssets(command.Host.Services); | ||
| command.ModuleContext = CreateModuleContext(command.Host.Services); |
- Add an integration test that serves a module's static web asset over HTTP
(published .modules/{module} layout in a temp content root) - verified to fail
if the asset composition is removed.
- Fail fast (GetRequiredService) instead of silently no-op'ing when the host env
or configuration is missing.
- Document why UseStaticWebAssets is deliberately NOT gated on IsDevelopment: a
modules host defaults to Production under `dotnet run`, so an env gate would
never load the dev manifest and module assets would 404; both loaders are
no-ops when their input is absent.
|
Thanks — addressed in 2d776eb: Automated test (your strongest point): added Fail-fast: switched to Dev-vs-non-dev gating: I intentionally kept |
Resolves the follow-up left by #43: a web module hosted via
UseAspNetCoreMinimal()now serves its own static web assets (wwwroot/_content), on net6/8/9/10, in bothdotnet run(dev) anddotnet publishoutput.Approach
Each module's inner
WebApplicationsetsApplicationName= the module assembly, so the module's own static web assets manifest is already root-mapped. AfterBuild()the handler composes those assets onto the module'sWebRootFileProvider:StaticWebAssetsLoader.UseStaticWebAssets— dev (reads{module}.staticwebassets.runtime.json)ModuleWebAssetsLoader.UseModuleAssets— published ({contentRoot}/wwwroot/.modules/{module})No
.modules/{module}prefix stripping is needed and each module host serves only its own assets (cleaner isolation than the classic shared-host filtering). Assets are served through the classicUseStaticFilespipeline; theWebRootFileProvideris resolved lazily by the middleware after build.Why not
MapStaticAssets(endpoint-based): the module serves viaUseStaticFiles, the endpoint routes are fingerprinted, and scoping per-module endpoints into a sub-host is more invasive. The file-provider path is simpler and matches how Hosuto modules have always served assets.Verification
samples/dotnet/minimal:GET /css/site.css→ 200 in dev and in published output (GET /stays 200). README updated to reflect the now-working behavior. All existing suites green on net6/8/9.Note: an automated static-web-assets test needs a referenced Razor library with build-generated manifests; the sample serves as the end-to-end validation here.