Migrer une application ASP.NET Web Forms vers ASP.NET Core : la méthode progressive
Beaucoup d'intranets, d'extranets métier et d'applications de gestion tournent encore sous ASP.NET Web Forms, souvent écrits il y a quinze ans et maintenus par une équipe réduite. Ils fonctionnent, mais ils sont liés à .NET Framework et à Windows Server. Passer à ASP.NET Core n'est pas une mise à jour : c'est une réécriture de la couche web. Bien conduite, elle peut pourtant se faire page par page, sans « grand soir ».
Faut-il vraiment migrer, et quand ?
Commençons par dissiper un malentendu. .NET Framework 4.8 et 4.8.1 n'ont pas de date de fin de support annoncée : Microsoft les traite comme des composants de Windows, pris en charge tant que la version de Windows qui les héberge l'est (voir la fiche de cycle de vie de .NET Framework). Une application Web Forms à jour de ses correctifs n'est donc pas, en soi, une faille béante.
Les raisons de migrer sont ailleurs : Web Forms ne reçoit plus de nouveautés, les bibliothèques tierces abandonnent .NET Framework les unes après les autres, l'hébergement est limité à IIS sous Windows, et il devient difficile de recruter des développeurs qui veulent travailler sur du ViewState. Côté cible, la version à viser aujourd'hui est .NET 10, version à support long (LTS) sortie le 11 novembre 2025 et prise en charge jusqu'au 14 novembre 2028 ; .NET 8 et .NET 9 arrivent tous deux en fin de support le 10 novembre 2026, selon la politique de support de .NET.
Ce qui ne passe pas d'un monde à l'autre
ASP.NET Core n'embarque pas l'assembly System.Web. Tout ce qui en dépend doit être remplacé :
- Les pages .aspx et leurs contrôles serveur (
GridView,UpdatePanel,SqlDataSource…) : il n'existe aucun moteur Web Forms dans ASP.NET Core. - Le ViewState et le cycle de vie de la page (
Page_Load,IsPostBack, événements de clic côté serveur) : la logique qui s'appuie dessus doit être réécrite autour de requêtes GET et POST explicites, ou de composants Blazor. Global.asax, les modules et gestionnaires HTTP : ils deviennent des intergiciels (middleware) dansProgram.cs.web.config: la configuration applicative passe dansappsettings.json, les variables d'environnement et, pour les secrets, un coffre adapté.- La session : elle existe dans ASP.NET Core, mais son API est différente et elle ne stocke que des octets ; les objets doivent être sérialisés.
Choisir la cible : Razor Pages, MVC ou Blazor
Pour une application organisée en pages et formulaires, Razor Pages est souvent le choix le plus naturel : un fichier .cshtml et sa classe PageModel jouent un rôle proche du couple .aspx et code-behind, sans ViewState. MVC convient mieux quand l'application expose aussi une API ou que l'équipe le pratique déjà.
Blazor, enfin, est le modèle dont la logique ressemble le plus à Web Forms : des composants réutilisables, des gestionnaires d'événements écrits en C#, un état conservé entre deux interactions. Microsoft a d'ailleurs publié un livre gratuit, Blazor pour les développeurs ASP.NET Web Forms, qui fait correspondre les notions une à une. Attention cependant à ne pas reproduire l'architecture d'origine par réflexe : en mode serveur interactif, chaque utilisateur maintient une connexion permanente, ce qui change la façon de dimensionner les serveurs.
Rien n'oblige à choisir un seul modèle : une même application ASP.NET Core peut servir des Razor Pages pour les écrans de saisie classiques et des composants Blazor pour un tableau de bord.
La méthode progressive : un proxy devant l'ancienne application
Pour une application de production, Microsoft recommande une migration incrémentale, inspirée du motif dit du « figuier étrangleur » : une nouvelle application ASP.NET Core est placée devant l'ancienne et reçoit toutes les requêtes. Celles qu'elle sait traiter, elle les traite ; les autres, elle les transmet à l'application Web Forms grâce au proxy inverse YARP. On déplace ensuite les écrans un par un, et l'ancienne application rétrécit jusqu'à disparaître. La démarche est décrite dans le guide Prise en main de la migration incrémentielle.
Le cœur de la configuration tient en quelques lignes. Le point important est la route « attrape-tout », placée en dernier, qui renvoie vers l'ancienne application tout ce qui n'a pas encore été migré :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSystemWebAdapters(); // paquet Microsoft.AspNetCore.SystemWebAdapters
builder.Services.AddHttpForwarder(); // YARP
var app = builder.Build();
app.UseRouting();
app.UseSystemWebAdapters();
app.MapRazorPages(); // les écrans déjà migrés
// Tout le reste part vers l'application Web Forms d'origine
app.MapForwarder("/{**catch-all}", app.Configuration["AncienneAppUrl"]!)
.Add(static b => ((RouteEndpointBuilder)b).Order = int.MaxValue);
app.Run();
La documentation de configuration de l'application distante détaille la partie enregistrée côté .NET Framework, notamment la clé partagée qui authentifie les échanges entre les deux applications.
Partager la session et l'authentification pendant la transition
Le principal obstacle d'une migration progressive est l'état partagé. Si l'utilisateur se connecte sur une page Web Forms puis arrive sur une page Razor, il doit rester connecté ; si son panier est en session, il doit le retrouver. Les adaptateurs System.Web proposent pour cela une session distante et une authentification distante : la nouvelle application interroge l'ancienne, qui reste la source de vérité. Les détails sont dans les pages consacrées à la session et à l'authentification.
Deux conditions pratiques en découlent : tout ce qui est placé en session doit être sérialisable (fini les objets DataTable ou les connexions stockées « pour gagner du temps »), et il faut déclarer explicitement quelles clés de session sont partagées et avec quel type.
Les bibliothèques d'abord, des feuilles vers le tronc
La logique métier qui vit dans des bibliothèques de classes doit pouvoir servir aux deux applications pendant la transition. Le guide de Microsoft recommande de les faire cibler .NET Standard 2.0 quand c'est possible, sinon de les compiler à la fois pour .NET Framework et pour .NET 8 ou ultérieur, et de les mettre à niveau dans l'ordre des dépendances : d'abord celles qui ne dépendent d'aucune autre, l'application web en dernier. Une bibliothèque qui utilise HttpContext.Current peut remplacer sa référence à System.Web par le paquet des adaptateurs, ce qui évite de la réécrire immédiatement.
Avant même ce travail, deux chantiers préparatoires rapportent beaucoup : convertir les projets au format « SDK » et passer de packages.config à PackageReference, puis éliminer le code mort et les avertissements de compilation sur les API obsolètes.
Faire l'inventaire avant d'écrire une ligne
Une migration réussie commence par une liste. Pour chaque page .aspx, notez : les contrôles tiers utilisés (une suite de composants commerciale sans équivalent récent peut à elle seule imposer une refonte d'écran), les sources de données (SqlDataSource déclaratif, ADO.NET, procédures stockées), l'usage de la session et du cache, les droits d'accès, et la fréquentation réelle d'après les journaux IIS. Les écrans peu consultés et simples sont de bons candidats pour roder la chaîne ; les écrans critiques viennent ensuite, quand l'équipe maîtrise le nouvel environnement.
Pour la couche de données, c'est le moment de décider si l'on conserve ADO.NET (qui fonctionne très bien sous .NET 10 avec Microsoft.Data.SqlClient) ou si l'on adopte Entity Framework Core, sujet de notre article Entity Framework Core pour les développeurs ADO.NET.
Pièges fréquents dans une application française
- La culture. Une application Web Forms héritait souvent de la culture du serveur IIS, réglée en français. Sous ASP.NET Core, configurez explicitement l'intergiciel de localisation avec
fr-FR, faute de quoi les dates s'afficheront au format américain et la virgule décimale sera refusée par la validation. En conteneur Linux, vérifiez que les bibliothèques ICU sont présentes : sans elles, .NET bascule en mode de globalisation invariante et les formats français disparaissent. Server.TransferetResponse.Redirectau milieu d'un événement : le premier n'a pas d'équivalent direct, le second doit devenir un retour de méthode (RedirectToPage).- Les chemins en
~/résolus par les contrôles serveur : dans Razor, les Tag Helpers les gèrent, mais pas les chaînes construites à la main en C#. - L'authentification Windows des intranets : elle reste disponible (Negotiate), mais son déploiement hors d'IIS demande une configuration Kerberos à prévoir tôt avec l'équipe système.
En résumé
Placez une application ASP.NET Core devant l'existant, faites-lui transmettre par défaut toutes les requêtes à Web Forms, partagez session et authentification, puis migrez écran par écran en commençant par les plus simples. Chaque étape est livrable, réversible et testable en production réelle. C'est plus long sur le papier qu'une réécriture complète ; dans les faits, c'est presque toujours la seule qui aboutit.
Documentation officielle
- Microsoft Learn : Migrer d'ASP.NET Framework vers ASP.NET Core
- Microsoft Learn : Outils de migration depuis ASP.NET MVC, Web API et Web Forms
- Microsoft Learn : Migrer de Web Forms vers Blazor
- Projet YARP : documentation du proxy inverse
- Microsoft : politique de support de .NET et cycle de vie de .NET Framework