ASP.NET Core .NET 11 Samples
Sample projects demonstrating new ASP.NET Core features across the .NET 11 preview releases, built for the ASP.NET Community Standup livestream.
Projects
BlazorFeatures
Blazor Web App (Interactive Server/WebAssembly) demonstrating new Blazor components and features. Demos are grouped by the preview that introduced them.
Preview 1
- EnvironmentView (
/environment-view) — Conditional rendering based on hosting environment - Label Component (
/label-demo) — Accessible form labels with[Display]attribute support - DisplayName Component (
/displayname-demo) — Display property names from metadata attributes - QuickGrid OnRowClick (
/quickgrid-onrowclick) — Row click event handling - Navigation Features (
/navigation-features/overview) —RelativeToCurrentUriandGetUriWithFragment() - MathML Support (
/mathml-demo) — Proper MathML namespace in interactive rendering - InvokeVoidAsync() Analyzer (
/invoke-void-async-analyzer) — JSInterop best-practices analyzer
Preview 2
- TempData for Blazor SSR (
/tempdata-demo) —TempDataflash messages and multi-page flows - Label ID (Interactive) (
/label-interactive-demo) —Labelgenerates matchingid/forin interactive mode
Preview 3
- Variable-Height Virtualize (
/virtualize-demo) —Virtualizewith variable-height items
Preview 4
- SupplyParameterFromTempData (
/tempdata) —[SupplyParameterFromTempData]one-shot flash messages - Virtualize AnchorMode (
/virtualize-anchor) —AnchorMode+ItemComparerkeep the viewport stable - Circuit Pause (
/circuit-pause) — Server-initiated circuit pause/resume
Preview 5
- Client-side Validation (
/client-validation) — Validation that runs on the client - Async Validation (
/async-validation) — Async form validation with localized messages. Updated in Preview 6 to use async DataAnnotations (AsyncValidationAttribute), the same hooksApiFeaturesuses for minimal APIs - QuickGrid SSR (
/quickgrid-ssr) — QuickGrid in statically rendered pages - Session Parameter (
/session-parameter) —[SupplyParameterFromSession]
Preview 6
- C# Unions in Blazor (
/unions-demo) —union ToastMessage(string, RenderFragment)as a single "text or template" component parameter, plusDynamicComponentwith a boxed-union parameter. Requires<LangVersion>preview</LangVersion>and<EnablePreviewFeatures>true</EnablePreviewFeatures>. The union deliberately omits aMarkupString(raw HTML) case to avoid an XSS footgun. Current Razor limitations for union-typed parameters: the literal-attribute shortcut (Content="hello") doesn't compile — use the expression formContent="@("hello")"(dotnet/razor#13188) — and child-content markup doesn't populate aRenderFragmentcase (dotnet/razor#13200). See the design note: aspnet/specs#782. TheToastcomponent andToastMessageunion live in the sharedSharedComponentsRazor Class Library (referenced by both the Server and WebAssembly apps). - Virtualize scroll-to-item (
/virtualize-scroll) —Virtualize<TItem>.InitialIndexopens a large list at a given item, andScrollToIndexAsyncscrolls to any item on demand (dotnet/aspnetcore#66753). - Configure client from server (
/browser-options) —WithBrowserOptionssets client-side startup behavior (log level, reconnection, DOM preservation) from the server in C# instead of hand-writtenBlazor.startJavaScript; the page reads the resolved options withHttpContext.GetBrowserOptions()(dotnet/aspnetcore#67337). - Automatic CSRF protection (
/csrf-protection) — an SSR form protected automatically by the new cross-origin checks (Sec-Fetch-Site/Origin) with no antiforgery token and noapp.UseAntiforgery()(dotnet/aspnetcore#66585). The separateCsrfAttackerSiteproject forges a cross-site POST against this form.
BlazorFeatures.E2E.Tests
End-to-end tests for the BlazorFeatures app using the new
Microsoft.AspNetCore.Components.Testing library (Preview 4), which combines xUnit v3
collection fixtures, a YARP reverse proxy, and Playwright with Blazor-aware wait helpers.
See BlazorFeatures.E2E.Tests/README.md for details.
BlazorWasmFeatures
Standalone Blazor WebAssembly app demonstrating WASM-specific features:
- IHostedService Support — Background services running in the browser
- Environment Variables — Access environment variables via
IConfiguration - Web Worker (
/web-worker) — Offload CPU-intensive work to a Web Worker running a separate .NET runtime (uses theWebWorkerDemolibrary) - C# Unions (Preview 6) (
/unions-demo) — Verified to work end-to-end in a published, trimmed WASM build (default ILLink trimming):ToastMessagerendering of thestringandRenderFragmentcases. - Gateway backend proxy (Preview 6) (
/weather) — The dev-time Blazor Gateway (Microsoft.AspNetCore.Components.Gateway) proxies the client'sapi/weathercalls to the separateBackendApiservice via YARP. Because the WASM client only ever makes same-origin requests to the gateway, no CORS configuration is required on the client or the backend. The proxy route is supplied to the gateway through theReverseProxyconfig keys inProperties/launchSettings.json. Requires the gateway to run both the app and the backend (see Running the Samples).
WebWorkerDemo
Reusable Razor class library that wires up a [JSExport]/[JSImport] Web Worker host so
Blazor WebAssembly apps can run .NET work off the UI thread. Consumed by BlazorWasmFeatures.
SharedComponents
Generic Razor Class Library for components and types shared across the sample apps. Currently
hosts the Toast component and the ToastMessage C# union, consumed by both BlazorFeatures
(Server) and BlazorWasmFeatures (WebAssembly).
ApiFeatures
Web API (minimal APIs) demonstrating framework features:
- OpenAPI 3.2 (Preview 2) —
OpenApiVersion = OpenApi3_2 - Validation source generator (Preview 2) — Handles
JsonElement/Dictionaryindexer properties - Native OpenTelemetry tracing (Preview 2) — Framework emits HTTP semantic-convention tags by default
- Zstandard response compression (Preview 3) — zstd as a default provider (
zstd > br > gzip) - HTTP QUERY method (Preview 3) — GET-like requests with a body for complex searches (
QUERY /search) - FileContentResult/FileStreamResult in OpenAPI (Preview 4) — Documented as
{ type: string, format: binary } - Endpoint filters observe binding failures (Preview 4) — Filter pipeline runs even when parameter binding fails
- Enum parameter naming in OpenAPI (Preview 5) — Non-body enum params keep their C# names; array schema IDs use valid names
- Kestrel trailer header timeout (Preview 5) —
RequestHeadersTimeoutapplies to HTTP/2 and HTTP/3 trailer frames - C# unions (Preview 6) — union return types are described with
anyOfin OpenAPI and serialized by their active case (GET /pets/{id}); a union body binds by JSON token type (POST /pets/adopt) - Async validation (Preview 6) —
AsyncValidationAttribute(POST /register) andIAsyncValidatableObject(POST /reservations) run during minimal-API validation viaAddValidation()
SignalRFeatures and SignalRClient
SignalR authentication-refresh demo for .NET 11 Preview 6:
- Authentication refresh (Preview 6, dotnet/aspnetcore#67400) — the server enables
EnableAuthenticationRefreshon/clock, and the .NET client refreshes its bearer token without dropping the hub connection. Tokens last 45 seconds so an automatic refresh is visible during a short run. SignalRFeatureshosts the JWT bearer-secured/clockhub, the/token?user=aliceissuer, and/promote?user=aliceonhttps://localhost:7110. The client also calls/reset?user=aliceat startup, so the demo can be re-run repeatedly against one server process.SignalRClientstreams clock ticks for 50 seconds and prints auth-refresh callbacks plus a success summary. Eight seconds in it promotesalicetoadminand callsRefreshAuthenticationAsync(), so the new role shows up on the next tick over the same connection.
Security notes (this sample deliberately simplifies auth; don't copy these into production):
- The
/tokenendpoint issues a signed JWT for any requested username with no credential check, and/promoteand/resetchange the admin role for anyone who asks — all three stand in for real sign-in and real entitlement management. They are registered only in Development so a copy of this code can't expose them when deployed. Real apps authenticate the user (ASP.NET Core Identity, Microsoft Entra ID, or another IdP) and issue tokens from that trusted source; validate them withJwtBearerOptions.Authorityinstead of a local key. - The signing key is generated per process with
RandomNumberGenerator.GetBytes(32), so no key material is checked into this repo — the same approach used by the SignalRJwtSampleindotnet/aspnetcore. Restarting the server invalidates tokens from the previous run. - Enabling
EnableAuthenticationRefreshhands principal-change policy to the app. SignalR normally rejects a request whose user differs from the one a connection is bound to; for connections that span requests (long polling, stateful reconnect) that transport-level check is skipped once refresh is enabled, becauseOnAuthenticationRefreshis now the place to make that call. The hub layer still aborts a connection whose user identifier changes, so an identity swap never silently succeeds — but that abort happens after the refresh request has already returned success. This sample'sOnAuthenticationRefreshcompares thesubclaim ofPreviousUserandNewUserand rejects the refresh with403while it's still in flight, so the client gets an actionable error and keeps running on its current token. - What the sample does follow:
[Authorize]on the hub, HTTPS, full token validation (issuer/audience/key/lifetime), an identity check on refresh, andCloseOnAuthenticationExpirationso a connection that isn't refreshed is closed at token expiry.
BackendApi
Minimal Web API that serves weather data at /api/weather. It is the backend service that
BlazorWasmFeatures calls through the Blazor Gateway's YARP proxy. It has no CORS
configuration on purpose — the browser only talks to the gateway (same origin), and the
gateway-to-backend hop happens server-side.
CsrfAttackerSite
A deliberately separate origin for the automatic CSRF demo. It serves a single static page
(a fake "you won a prize" site) whose hidden form posts a funds transfer to BlazorFeatures
at http://localhost:5059/csrf-protection. Browse to it at http://127.0.0.1:8080 —
127.0.0.1 is a different site from the bank's localhost, so the browser marks the request
Sec-Fetch-Site: cross-site and .NET 11's automatic CSRF protection rejects it with 400.
See Automatic CSRF protection demo for how to run it.
Running the Samples
Requires the .NET 11 Preview 6 SDK (11.0.100-preview.6.26359.118) or later.
dotnet build
# Run a project, e.g. the Blazor Web App:
dotnet run --project BlazorFeatures
Automatic CSRF protection demo
Run the Blazor app and the attacker site in separate terminals, then browse to the attacker at
http://127.0.0.1:8080 (note 127.0.0.1, not localhost, so it's a different site):
# Terminal 1 — the "bank" app on http://localhost:5059
dotnet run --project BlazorFeatures --launch-profile http
# Terminal 2 — the attacker site on http://127.0.0.1:8080
dotnet run --project CsrfAttackerSite --launch-profile http
Submitting the transfer form on /csrf-protection directly (same-origin) succeeds. Clicking
"Claim your prize" on the attacker page posts the same form from a different site; open the
browser dev tools Network tab (enable "Preserve log") to see the request carry
Sec-Fetch-Site: cross-site and get rejected with 400.
SignalR authentication refresh
Start the server, then run the client. In Visual Studio, right-click SignalRFeatures → Debug →
Start Without Debugging, then set SignalRClient as the startup project and Ctrl+F5. From the
command line:
# Terminal 1 — SignalR server on https://localhost:7110
dotnet run --project SignalRFeatures
# Terminal 2 — ticks continue across a role change and a token expiry
dotnet run --project SignalRClient
The client shows roles=user for the first few ticks. At 8 seconds it promotes alice and calls
RefreshAuthenticationAsync(), so the next tick reads roles=user,admin on the same connection id.
Around 40 seconds the automatic refresh fires and token-exp moves forward.
To show the previous behavior for contrast, disable client auto-refresh. Because the hub also sets CloseOnAuthenticationExpiration, the connection closes when the 45-second token expires:
dotnet run --project SignalRClient -- --no-refresh --duration-seconds 60
To show the identity check, have the client request its refresh token for a different user. The server's OnAuthenticationRefresh compares the sub claim and rejects the refresh with 403, so the connection is never re-bound to bob:
dotnet run --project SignalRClient -- --refresh-as bob --duration-seconds 55
Blazor Gateway backend proxy (no CORS)
To try the standalone WASM app calling a backend through the gateway proxy, run the backend
and the WASM app (hosted by the gateway) together, then browse to /weather:
# Terminal 1 — backend service on http://localhost:5100
dotnet run --project BackendApi --launch-profile http
# Terminal 2 — WASM app hosted by the Blazor Gateway on http://localhost:5090
dotnet run --project BlazorWasmFeatures --launch-profile http
Open http://localhost:5090/weather. The client fetches api/weather from the gateway origin,
and the gateway proxies it to BackendApi via YARP. The proxy route/cluster (and the backend
address) are configured with ReverseProxy__* environment variables in
BlazorWasmFeatures/Properties/launchSettings.json.
All packages are published on nuget.org, so no extra NuGet feeds are required.