.NET Aspire sample
The AppHost
runs Redis (cache), Valkey (valkey), and an HTTP service (api) that uses Aspire.Respire
and its HybridCache and output-cache companions. WithReference injects the connection strings
and credentials, and WaitFor starts the service once both servers are healthy, so the app has no
ports or passwords in its configuration. For configuration details, see
Aspire integration.
Requirements and build
- The .NET 10 SDK from
global.json(10.0.401 or a later compatible band) and runtime. The sample targetsnet10.0only; the integration itself also supports .NET 8. - Aspire CLI 13.6 (the repository uses Aspire 13.6.1 hosting packages).
- A running Docker-compatible runtime. The first start downloads the Redis 8.10 and Valkey 9 images.
- PowerShell 7 for the optional
Smoke.ps1helper. - A trusted ASP.NET Core development certificate for the HTTPS dashboard.
From the repository root:
dotnet dev-certs https --trust
dotnet build samples/Respire.Samples.Aspire.AppHost -c Release -f net10.0
Building the AppHost also builds the HTTP service.
Start, readiness, and dashboard
Run the remaining Aspire commands from the AppHost directory:
cd samples/Respire.Samples.Aspire.AppHost
aspire start --non-interactive
aspire wait cache --non-interactive --timeout 120
aspire wait valkey --non-interactive --timeout 120
aspire wait api --non-interactive --timeout 120
aspire describe --non-interactive
- Add
--isolatedtoaspire startin a worktree or to run several instances. It randomizes endpoints and isolates AppHost secrets. - Endpoints are dynamic. Use the dashboard login URL printed by
start, and theapiHTTP URL fromdescribeor the dashboard Resources page. - Keep the dashboard login token private; do not paste it into issues or commits.
aspire wait api checks /health, which reports Redis and Valkey readiness. The service sends
a bounded PING through both clients at startup, because health checks never connect an unused
client.
Example requests and cache smoke check
Set $baseUrl to the HTTP URL, without a trailing slash:
$baseUrl = Read-Host 'Paste the api HTTP URL from aspire describe'
Invoke-WebRequest "$baseUrl/health" # Healthy, HTTP 200
Invoke-RestMethod "$baseUrl/ping" # Redis PING latency
Invoke-RestMethod "$baseUrl/valkey" # Keyed Valkey PING latency
Invoke-RestMethod "$baseUrl/distributed/example" # value equals stored
Invoke-WebRequest "$baseUrl/hybrid/example-hybrid"
Invoke-WebRequest "$baseUrl/hybrid/example-hybrid/distributed"
Invoke-WebRequest "$baseUrl/output"
pwsh -NoProfile -File ./Smoke.ps1 -BaseUrl $baseUrl
/distributed/{key}writes a value and reads it back from Redis./hybrid/{key}returns the same value on repeat. Its/distributedvariant skips L1 and throws on a miss, proving the value is in Redis.- Use different keys for distributed and HybridCache requests; their stored formats differ.
/outputreturns the cached response until its policy expires.Smoke.ps1uses fresh keys and fails on any health, connection, or cache mismatch.
Dashboard telemetry and failure behavior
After making requests, select api in the dashboard:
- Structured logs show requests and cache hits and misses.
- Traces show
Respirespans, includingPINGand cache commands. - Metrics show the
Respiremeter. The sample callsRespireMetrics.Configurebefore creating clients to addRespireMetricGroups.Command(command duration).
Telemetry is exported in batches, so new data takes a few seconds to appear. The CLI shows the same data:
aspire otel logs api --non-interactive --limit 20
aspire otel spans api --non-interactive --limit 20
To demonstrate unhealthy readiness, stop only this sample's Redis resource:
aspire resource cache stop --non-interactive
aspire wait cache --status down --non-interactive --timeout 30
Invoke-WebRequest "$baseUrl/health" -SkipHttpErrorCheck
aspire describe api --non-interactive
aspire resource cache start --non-interactive
aspire wait cache --non-interactive --timeout 120
aspire wait api --non-interactive --timeout 120
Once the client notices the disconnect, /health returns HTTP 503 Unhealthy and api is
marked unhealthy; stopping Valkey does the same. The client recovers according to its reconnect
policy. Cached L1 and output responses do not prove Redis is up; use /health and the distributed
round trip.
Owned-resource cleanup
From the same AppHost directory:
aspire stop --non-interactive
This stops the AppHost, service, dashboard, and this session's Redis and Valkey containers (no
persistent volumes, so cache data is discarded). Other AppHosts and containers are untouched. If
startup fails, check aspire logs api --non-interactive and aspire describe --non-interactive,
then stop the sample before rebuilding.
For other hosting models, see the ASP.NET Core sample and the Cluster and Sentinel samples.