[Static web assets] Support for fallbacks and default files
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
## Summary
Add support for default file serving (``) and SPA fallback routing (``) in the StaticWebAssets SDK, providing feature parity with `UseDefaultFiles()` and `MapFallbackToFile()` for apps using `MapStaticAssets()`.
## Motivation and goals
- **Default Files Gap**: Currently, `MapStaticAssets()` doesn't support serving default documents (like `index.html`) when a directory path is requested. Developers using the new `.NET 9+` static assets pipeline must either:
- Continue using `UseDefaultFiles()` middleware before `MapStaticAssets()` (mixing paradigms)
- Manually configure additional routes for their default pages
- **SPA Fallback Gap**: Single Page Applications (SPAs) require fallback routing where non-file requests (e.g., `/about`, `/users/123`) are served by a single entry point file (typically `index.html`). The existing `MapFallbackToFile("index.html")` works with `UseStaticFiles()` but not with the optimized `MapStaticAssets()` pipeline.
- **Consistency**: The StaticWebAssets SDK should support common static file scenarios at build-time, enabling the full optimization benefits (compression, fingerprinting, ETags) for these patterns.
## In scope
1. **Default Files Support (``)**: When a request matches a directory path (e.g., `/` or `/admin/`), serve a configured default file from that directory
- Support configuring one or more default file names
- Generate additional endpoints at build time for directory-to-file mappings
2. **SPA Fallback Support (``)**: Add a catch-all fallback endpoint `{**slug}` that serves a specified file for non-file requests
- Useful for client-side routing in SPAs (React, Angular, Vue, Blazor WASM standalone)
- Should work with fingerprinted assets while serving the non-fingerprinted fallback path
## Out of scope
- Directory browsing functionality (`UseDirectoryBrowser`) - this is a runtime-only feature that lists directory contents dynamically
- Custom request path mappings (serve files from non-webroot locations) - can be achieved with existing `PhysicalFileProvider` configuration
- `UseFileServer()` combined middleware replacement - out of scope for initial implementation
## Risks / unknowns
1. **Endpoint Ordering**: The fallback endpoint must have the lowest priority (`int.MaxValue` order) to avoid matching before other routes. Need to ensure this integrates correctly with other mapped endpoints.
2. **Non-file Route Constraint**: The fallback pattern should use `:nonfile` route constraint (like `MapFallbackToFile` does) to allow requests with file extensions to 404 naturally. Need to validate this works with the generated endpoints.
3. **Fingerprinted vs Non-fingerprinted**: Default files and fallback files may reference fingerprinted assets internally. The entry point itself typically shouldn't be fingerprinted (need stable URL), but should still benefit from compression and ETags.
4. **Multiple Entry Points**: Some apps may have multiple SPAs or sections with different fallback files (e.g., `/admin/{**slug}` → `admin/index.html`). Should we support path-specific fallbacks?
5. **Developer Confusion**: Developers might set both properties incorrectly. Clear documentation needed on:
- Default files: "Serve `index.html` when user visits `/`"
- Fallback: "Serve `index.html` for any unmatched route like `/about` or `/products/123`"
## Examples
### Default Files Configuration
```xml
index.html
```
**Behavior**:
- Request to `/` → serves `wwwroot/index.html`
- Request to `/admin/` → serves `wwwroot/admin/index.html` (if exists)
**Equivalent middleware today**:
```csharp
app.UseDefaultFiles();
app.MapStaticAssets();
```
### SPA Fallback Configuration
```xml
index.html
```
**Behavior**:
- Request to `/about` → serves `wwwroot/index.html` (no file extension, no matching route)
- Request to `/products/123` → serves `wwwroot/index.html`
- Request to `/styles.css` → serves the actual CSS file (file extension, not fallback)
- Request to `/images/logo.png` → serves the actual image (file extension, not fallback)
**Equivalent middleware today**:
```csharp
app.MapStaticAssets();
app.MapFallbackToFile("index.html");
```
### Combined Configuration (Typical SPA)
```xml
index.html
index.html
```
### Advanced: Multiple Default Files (future consideration)
```xml
index.html;default.html
```
### Comparison with Current Static Files Middleware
| Feature | StaticFiles Middleware | Proposed StaticWebAssets SDK |
|---------|----------------------|------------------------------|
| Default files | `app.UseDefaultFiles()` | `` |
| Fallback routing | `app.MapFallbackToFile("index.html")` | `` |
| Compression | ❌ Runtime only | ✅ Build-time Gzip/Brotli |
| Fingerprinting | ❌ Not supported | ✅ Content-based fingerprinting |
| ETags | Runtime computed | Build-time computed |
Contributor guide
Research direction
Start by tracing the StaticWebAssets SDK entry point and the existing MapStaticAssets(), UseDefaultFiles(), and MapFallbackToFile() behaviors described here. Resolve the endpoint ordering, nonfile constraint, fingerprinting, and configuration questions before implementation. Done means supporting the requested default-file and SPA-fallback scenarios while leaving the listed out-of-scope features unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- build-system, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100