Projet réel : ASP.NET Core Web API, .NET 8, packages Stimulsoft.PDF.Forms (génération de formulaires PDF côté serveur, en paire avec le package Angular stimulsoft-forms côté client) et Microsoft.EntityFrameworkCore.Sqlite (base de données SQLite locale).
Topologie retenue : cette API est déployée sur un site/domaine séparé de l'application Angular (ex. api.mondomaine.local vs questionnaire.mondomaine.local). Cela implique de configurer explicitement CORS pour que le frontend Angular soit autorisé à appeler l'API — voir étape 5.
Ce guide décrit le déploiement d'une API ASP.NET Core (.NET 8) sur un Windows Server via IIS, en tant que backend séparé pour l'application Angular (déjà couverte dans un guide dédié). L'API génère des formulaires PDF via Stimulsoft.PDF.Forms et persiste ses données dans une base SQLite locale au serveur (fichier unique, pas de serveur de base de données séparé).
Comme pour tout ASP.NET Core, IIS héberge l'application in-process via le module ASP.NET Core (ANCM) : pas de process séparé à surveiller, pas de reverse proxy manuel, IIS démarre/redémarre l'API automatiquement.
Schéma : Angular (autre site/domaine) → appels HTTPS avec en-têtes CORS → IIS (port 443, certificat SSL) → module ANCM → API .NET (Stimulsoft.PDF.Forms + EF Core) → fichier SQLite local sur le serveur.
Point d'attention spécifique à ce projet, à garder en tête tout du long : le fichier de base SQLite ne doit jamais se trouver dans le dossier qui sera écrasé à chaque déploiement (sous peine de perdre les données à la prochaine mise à jour) — voir étape 4.
api.mondomaine.local.# PowerShell, en tant qu'administrateur Install-WindowsFeature -Name Web-Server -IncludeManagementTools
Indispensable pour qu'IIS sache exécuter une application .NET 8 (sans elle, erreur 500.19 ou 502.5).
net stop was /y
net start w3svc
AspNetCoreModuleV2.
Comme l'API et l'Angular sont sur deux origines différentes, le navigateur bloquera par défaut les appels de l'Angular vers l'API tant que CORS n'est pas explicitement autorisé côté serveur. Dans Program.cs :
var builder = WebApplication.CreateBuilder(args); builder.Services.AddCors(options => { options.AddPolicy("AllowAngularApp", policy => { policy.WithOrigins("https://questionnaire.mondomaine.local") // origine exacte de l'Angular .AllowAnyHeader() .AllowAnyMethod(); // .AllowCredentials() si l'API utilise des cookies d'authentification }); }); // ... reste de la configuration ... var app = builder.Build(); app.UseCors("AllowAngularApp"); // ... app.MapControllers(); etc.
Remplacer l'URL par la vraie origine de production de l'Angular (protocole + domaine + port exact). Une politique CORS trop permissive (``AllowAnyOrigin``) est déconseillée dès lors que l'API traite des données de questionnaire.
C'est l'étape la plus importante à ne pas manquer sur ce projet. Si la chaîne de connexion SQLite par défaut (souvent Data Source=app.db dans appsettings.json) pointe vers un chemin relatif, le fichier se crée dans le dossier de l'application — c'est-à-dire exactement le dossier qui sera écrasé/synchronisé à chaque redéploiement. Résultat : toutes les réponses au questionnaire disparaissent au prochain déploiement.
D:\Data\questionnaire-api\.appsettings.Production.json (créé s'il n'existe pas encore, à côté d'appsettings.json dans le projet), définir un chemin absolu :{
"ConnectionStrings": {
"DefaultConnection": "Data Source=D:\\Data\\questionnaire-api\\app.db"
}
}
IIS AppPool\<NomDuPool>) les droits de lecture et écriture sur D:\Data\questionnaire-api\ (clic droit sur le dossier → Propriétés → Sécurité → Ajouter → saisir IIS AppPool\NomDuPool → Modifier).app.db avant toute mise à jour du schéma.Sauvegardes : un fichier SQLite se sauvegarde simplement en copiant le fichier.db(idéalement pool arrêté, ou via l'outilbackupde SQLite pour une copie à chaud cohérente). Mettre en place une tâche planifiée de copie régulière deD:\Data\questionnaire-api\app.dbvers un emplacement de sauvegarde.
dotnet publish -c Release -o ./publish
Le dossier publish/ contient les DLL, les dépendances (dont les assemblies Stimulsoft.PDF.Forms), et un web.config généré automatiquement configurant le module ANCM.
Vérifier queappsettings.Production.json(étape 4) et, le cas échéant,license.key(étape 8) sont bien présents dans ce dossier publié — les fichiers de configuration/licence ne sont inclus que s'ils sont correctement référencés dans le.csproj(propriété « Copy to Output Directory »).
Copier le dossier publish/ vers un dossier dédié, par exemple D:\Sites\questionnaire-api\ — bien distinct du dossier de données créé à l'étape 4.
robocopy publish D:\Sites\questionnaire-api /MIR
Rappel :/MIRsupprime côté serveur tout ce qui n'est pas dans le dossier source. Comme la base SQLite est désormais hors de ce dossier (étape 4), elle ne sera jamais affectée par cette commande — c'est précisément l'objectif.
api.mondomaine.local.'appsettings.Production.json est bien utilisé et que les pages d'erreurs détaillées de développement sont désactivées — ajouter dans le web.config (dans la balise <aspNetCore>) :<aspNetCore processPath="dotnet" arguments=".\QuestionnaireApi.dll" hostingModel="InProcess"> <environmentVariables> <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" /> </environmentVariables> </aspNetCore>
Si le code charge la licence par fichier (``Stimulsoft.Base.StiLicense.LoadFromFile(path)``), vérifier que ce fichier license.key est bien copié dans publish/ puis présent sur le serveur au chemin attendu. Si elle est chargée par code (clé en dur dans Program.cs), rien à déployer séparément, mais vérifier qu'elle n'est pas accidentellement liée à un environnement de développement uniquement (ex. clé d'essai).
La génération/soumission de formulaires PDF peut impliquer des payloads plus volumineux que la moyenne (documents, images intégrées). Si erreur 404.13 ou 413 :
<system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="52428800" /> <!-- 50 Mo, à ajuster --> </requestFiltering> </security> </system.webServer>
api.mondomaine.local (interne ou public selon l'exposition).Program.cs pour rediriger automatiquement le trafic HTTP restant vers HTTPS.New-NetFirewallRule -DisplayName "IIS HTTPS API" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow New-NetFirewallRule -DisplayName "IIS HTTP API" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow
https://api.mondomaine.local/swagger (si Swagger est activé pour un test ponctuel) ou un endpoint connu de l'API doit répondre.D:\Data\questionnaire-api\app.db grossit après une soumission).Activer temporairement les logs stdout dans le web.config en cas de problème :
<aspNetCore processPath="dotnet" arguments=".\QuestionnaireApi.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="InProcess" />
Créer le sous-dossier logs\ avec droits d'écriture pour le pool d'application. Consulter aussi l'Observateur d'événements Windows (Journaux Windows → Application) pour les erreurs de démarrage du module ANCM. Repasser stdoutLogEnabled à false une fois le diagnostic terminé.
D:\Sites\questionnaire-api\ et D:\Data\questionnaire-api\ aux seuls comptes nécessaires (déploiement, pool d'application).appsettings.Production.json ou license.key dans un dépôt Git public — les gérer comme des secrets de déploiement.dotnet publish -c Release -o ./publish.D:\Data\questionnaire-api\app.db vers un emplacement de sauvegarde.app_offline.htm à la racine de D:\Sites\questionnaire-api\ pour libérer proprement les fichiers verrouillés.robocopy publish D:\Sites\questionnaire-api /MIR (la base de données, hors de ce dossier, n'est pas affectée).app_offline.htm.| Élément | Statut |
|---|---|
| Rôle IIS installé | ☐ |
| .NET 8 Hosting Bundle installé, AspNetCoreModuleV2 vérifié | ☐ |
| Politique CORS configurée avec l'origine exacte de l'Angular | ☐ |
| Dossier de données SQLite créé hors du dossier de déploiement | ☐ |
| Chaîne de connexion pointant vers ce dossier (appsettings.Production.json) | ☐ |
| Droits NTFS du pool d'application sur le dossier de données | ☐ |
| Application publiée (dotnet publish -c Release) | ☐ |
| Fichiers copiés sur le serveur (web.config inclus) | ☐ |
| license.key présent sur le serveur si utilisé | ☐ |
| Limite de taille des requêtes ajustée si besoin | ☐ |
| Site créé dans IIS, pool en « No Managed Code », ASPNETCORE_ENVIRONMENT=Production | ☐ |
| Certificat SSL installé, binding https configuré | ☐ |
| Pare-feu : ports 80/443 ouverts | ☐ |
| Appel depuis l'Angular réel sans erreur CORS | ☐ |
| Génération de PDF testée de bout en bout | ☐ |
| Base SQLite vérifiée au bon emplacement, hors dossier de déploiement | ☐ |
| Sauvegarde de la base SQLite planifiée | ☐ |
| Swagger désactivé/protégé en production | ☐ |
| Symptôme | Piste de résolution |
|---|---|
| Erreur CORS dans la console navigateur | Vérifier l'origine exacte déclarée dans WithOrigins (protocole, domaine, port), et que app.UseCors(…) est bien appelé avant app.MapControllers(). |
| Les données disparaissent après un déploiement | La base SQLite était encore dans le dossier de déploiement écrasé par /MIR — la déplacer vers un dossier persistant (étape 4) et restaurer depuis une sauvegarde si disponible. |
| Erreur d'accès refusé sur le fichier .db | Droits NTFS insuffisants pour le compte IIS AppPool\<NomDuPool> sur le dossier de données. |
| Erreur HTTP 500.19 | web.config invalide ou Hosting Bundle absent. |
| Erreur HTTP 500.30 (in-process start failure) | Activer stdoutLogEnabled et consulter l'Observateur d'événements Windows pour l'exception exacte (souvent une erreur de connexion à la base ou de licence Stimulsoft au démarrage). |
| Erreur de licence Stimulsoft en production | license.key absent du dossier publié, ou chemin de chargement incorrect (voir étape 8.1). |
| Erreur 404.13 / 413 sur soumission de formulaire | Augmenter maxAllowedContentLength (étape 8.2). |
Document préparé le 18 août 2026.