Dans Aspire 9, "application .NET distribuée" signifiait exactement cela : .NET. Les services Python, backends Node.js ou binaires Go pouvaient être inclus comme conteneurs, mais sans réelle intégration, pas de découverte de services automatique, pas d'injection de chaînes de connexion, pas de support de debugging. À partir d'Aspire 13, cela a changé.
Ce que signifie "first class"
La différence entre un conteneur et un service de première classe dans l'AppHost n'est pas triviale. Un conteneur tourne, point final. Un service de première classe reçoit :
- Des chaînes de connexion automatiques : les références sont injectées comme variables d'environnement typées
- La découverte de services : les autres services le trouvent par son nom
- Le développement local sans Docker : le processus démarre directement sur l'hôte avec le runtime natif
- La génération automatique de Dockerfile : pour le déploiement
- L'intégration au dashboard : logs et traces apparaissent dans l'Aspire Dashboard
Cela s'applique à Python, JavaScript, Go et Bun depuis Aspire 13.
Python
Les services Python s'enregistrent via AddPythonApp. Aspire détecte automatiquement si le projet utilise uv, pip ou venv, puis configure l'environnement runtime en conséquence :
var ml = builder.AddPythonApp("inference", "../Inference")
.WithReference(db)
.WithHttpEndpoint(port: 8000);
Pour les services basés sur FastAPI et Uvicorn, il existe AddUvicornApp :
var api = builder.AddUvicornApp("fastapi", "../FastApiService", "main:app")
.WithReference(cache)
.WithExternalHttpEndpoints();
Les chaînes de connexion sont transmises comme variables d'environnement, lues côté Python via os.environ comme d'habitude. Le certificat SSL pour les connexions HTTPS entre services est automatiquement fourni dans SSL_CERT_FILE.
Aspire génère automatiquement un Dockerfile à partir du projet Python lors de aspire publish, sans écriture manuelle.
JavaScript et Node.js
AddJavaScriptApp remplace l'ancien AddNpmApp et prend en charge npm, Yarn et pnpm. La détection se fait automatiquement sur la base des lockfiles présents dans le dossier projet :
var frontend = builder.AddJavaScriptApp("frontend", "../Frontend", "npm run dev")
.WithReference(api)
.WithExternalHttpEndpoints();
Pour les frontends basés sur Vite :
var frontend = builder.AddViteApp("frontend", "../Frontend")
.WithReference(api);
À partir d'Aspire 13.4, Bun est disponible comme runtime :
var worker = builder.AddBunApp("worker", "../Worker", "src/index.ts")
.WithReference(queue);
Spécificité Node : le certificat SSL pour les connexions HTTPS internes est injecté via NODE_EXTRA_CA_CERTS, de sorte que fetch et les clients HTTPS puissent communiquer avec d'autres services Aspire sans configuration supplémentaire.
Go
Le support Go est arrivé dans Aspire 13.4 avec le package Aspire.Hosting.Go :
var service = builder.AddGoApp("goservice", "../GoService")
.WithReference(db)
.WithHttpEndpoint(port: 8080);
En développement local, Aspire démarre le service via go run. Le debugging est intégré via l'adaptateur dlv-dap pour VS Code. Pour le déploiement, Aspire génère un Dockerfile.
TypeScript AppHost, orchestration sans .NET
Toute personne orchestrant un paysage composé de services Python, Node et Go n'a plus besoin de .NET dans la toolchain AppHost à partir d'Aspire 13.4. L'AppHost peut être écrit entièrement en TypeScript :
import { createBuilder } from "@aspire/hosting";
import { addPostgres } from "@aspire/hosting-postgresql";
import { addPythonApp } from "@aspire/hosting-python";
const builder = await createBuilder(process.argv);
const db = addPostgres(builder, "db");
const inference = addPythonApp(builder, "inference", "../Inference")
.withReference(db);
const frontend = builder.addJavaScriptApp("frontend", "../Frontend", "npm run dev")
.withReference(inference);
await builder.build().run();
L'API TypeScript reflète la surface C# à l'identique, générée à partir des mêmes commentaires XML. Cela signifie que toute personne connaissant l'API C# se sentira immédiatement chez elle dans la version TypeScript.
Ce qui est encore en développement
Java figure sur la roadmap comme autre type de déploiement, mais n'est pas encore disponible. Rust est discuté, mais pas planifié. Les langages existants, Python, JavaScript, Go et TypeScript, sont stables à partir de 13.4 ; Blazor WebAssembly reste marqué comme preview.
Pièges connus
TypeScript AppHost : paramètre sans valeur par défaut qui bloque les pipelines CI
builder.addParameter('name') sans valeur par défaut explicite bloque aspire run dans les environnements non interactifs, comme les pipelines CI ou lorsque stdin n'est pas un TTY. Le processus se fige sans message d'erreur ni timeout. C'est particulièrement insidieux parce que cela fonctionne très bien localement dans un terminal.
// Hangs in CI:
const secret = await builder.addParameter('db-password');
// Correct: set a default
const secret = await builder.addParameter('db-password', { value: process.env.DB_PASSWORD ?? '' });
Le ticket (microsoft/aspire#17678) est toujours ouvert. En attendant sa résolution : définissez toujours une valeur par défaut, même vide.
Python et JavaScript sur Kubernetes : PublishAsDockerFile supprime les args du conteneur
Ajouter un callback PublishAsDockerFile à un service Uvicorn ou JavaScript, même vide, provoque la disparition des args générés dans le manifest Helm. Le conteneur démarre alors sans les paramètres de démarrage configurés. Pas d'erreur, pas de warning.
L'équipe Aspire a marqué le sujet comme silent-failure (microsoft/aspire#16874). Contournement : définir explicitement les args dans le callback, ou ne pas utiliser de callback du tout et fournir à la place un Dockerfile manuel.
