Mise à niveau de ASP.NET Framework vers ASP.NET Core

Pourquoi effectuer une mise à niveau vers la dernière version de .NET

ASP.NET Core est l’infrastructure web moderne pour .NET. Bien qu’ASP.NET Core ait de nombreuses similitudes avec ASP.NET dans le .NET Framework, il s’agit d’une nouvelle infrastructure entièrement réécrite. Les applications ASP.NET mises à jour vers ASP.NET Core peuvent bénéficier d’une amélioration des performances et de l’accès aux dernières fonctionnalités et fonctions de développement web.

Approches de mise à jour de ASP.NET Framework

La plupart des applications ASP.NET Framework non triviales doivent envisager d’utiliser l’approche de mise à niveau incrémentielle. Pour plus d’informations, consultez Mise à niveau incrémentielle d’ASP.NET vers ASP.NET Core.

Pour les applications MVC et l’API Web d’ASP.NET, consultez Apprendre à effectuer une mise à niveau à partir du MVC et de l’API Web d’ASP.NET vers le MVC d’ASP.NET Core. Pour les applications Web Forms d’ASP.NET Framework, consultez Apprendre à effectuer une mise à niveau d’ASP.NET Web Forms vers ASP.NET Core.

Modèles d’application web fiables

Consultez Le modèle d’application web fiable for.NETvidéos YouTube et l’article pour obtenir des conseils sur la création d’une application ASP.NET Core moderne, fiable, performante, testable, économique et évolutive, que ce soit à partir de zéro ou en refactorisant une application existante.

Différences de décodage d’URI entre ASP.NET et ASP.NET Core

ASP.NET Core présente les différences de décodage d’URI suivantes avec ASP.NET Framework :

ASCII Encoded ASP.NET Core ASP.NET Framework
\ %5C \ /
/ %2F %2F /

Lors du décodage de %2F sur ASP.NET Core :

  • Le chemin d’accès entier n’a pas de séquence d’échappement, sauf %2F, car sa conversion en / modifierait la structure du chemin d’accès. Il ne peut pas être décodé tant que le chemin d’accès n’est pas divisé en segments.

Pour générer la valeur pour HttpRequest.Url, utilisez new Uri(this.AspNetCoreHttpRequest.GetEncodedUrl()); pour éviter à Uri de mal interpréter les valeurs.

Migration des secrets utilisateur d'ASP.NET Framework vers ASP.NET Core

Consultez ce problème GitHub.

Cet article sert de guide de référence pour la migration d’applications ASP.NET vers ASP.NET Core.

Visual Studio a des outils permettant de migrer les applications ASP.NET vers ASP.NET Core. Pour plus d’informations, consultez Migration d’ASP.NET vers ASP.NET Core dans Visual Studio.

L’Assistant Mise à niveau de .NET est un outil en ligne de commande permettant d’effectuer une migration d’ASP.NET vers ASP.NET Core. Pour plus d’informations, consultez Vue d’ensemble de l’Assistant Mise à niveau de .NET et Mettre à niveau une application ASP.NET MVC vers .NET 6 avec l’Assistant de mise à niveau .NET.

Pour obtenir un guide de portage complet, consultez le livre électronique Portage d’applications existantes d’ASP.NET vers .NET Core.

Prérequis

Kit SDK .NET Core 2.2 ou version ultérieure

Frameworks cibles

Les projets ASP.NET Core permettent aux développeurs de cibler .NET Core, le .NET Framework ou les deux. Consultez Choix entre le .NET Core et le .NET Framework pour les applications serveur afin de déterminer quel est le framework cible le plus approprié.

Quand vous ciblez le .NET Framework, les projets doivent référencer des packages NuGet individuels.

Le ciblage du .NET Core vous permet d’éliminer de nombreuses références de packages explicites, grâce au métapackage ASP.NET Core. Installez le métapackage Microsoft.AspNetCore.App dans votre projet :

<ItemGroup>
   <PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

Quand le métapackage est utilisé, aucun package référencé dans le métapackage n’est déployé avec l’application. Le magasin de runtimes du .NET Core inclut ces composants. Ceux-ci sont précompilés pour améliorer les performances. Pour plus d’informations, consultez Métapackage Microsoft.AspNetCore.App pour ASP.NET Core.

Différences de structure de projet

Le format de fichier .csproj a été simplifié dans ASP.NET Core. Voici certains changements notables :

  • Il n’est pas nécessaire d’inclure explicitement les fichiers pour qu’ils soient considérés comme faisant partie du projet. Cela réduit le risque de conflits de fusion XML quand vous travaillez avec des équipes de grande taille.

  • Il n’existe aucune référence basée sur un GUID à d’autres projets, ce qui améliore la lisibilité du fichier.

  • Vous pouvez modifier le fichier sans devoir le décharger dans Visual Studio :

    Edit CSPROJ context menu option in Visual Studio 2017]

Remplacement du fichier Global.asax

ASP.NET Core a introduit un nouveau mécanisme pour le démarrage d’une application. Le point d’entrée des applications ASP.NET est le fichier Global.asax. Les tâches telles que la configuration de l’itinéraire ou l’inscription des filtres et des zones sont traitées dans le fichier Global.asax.

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

Cette approche couple l’application au serveur sur lequel elle est déployée d’une manière qui interfère avec l’implémentation. Pour y remédier, OWIN a donc été introduit afin d’optimiser l’utilisation de plusieurs frameworks à la fois. OWIN fournit un pipeline qui permet d’ajouter uniquement les modules nécessaires. L’environnement d’hébergement utilise une fonction Startup pour configurer les services et le pipeline de requêtes de l’application. Startup inscrit un ensemble d’intergiciels (middleware) auprès de l’application. Pour chaque requête, l’application appelle chacun des composants intergiciels (middleware) à l’aide du pointeur d’en-tête d’une liste liée à un ensemble existant de gestionnaires. Chaque composant intergiciel (middleware) peut ajouter un ou plusieurs gestionnaires au pipeline de traitement des requêtes. Pour ce faire, une référence doit être retournée au gestionnaire qui représente le nouvel en-tête de la liste. Chaque gestionnaire doit mémoriser et appeler le prochain gestionnaire de la liste. Avec ASP.NET Core, comme le point d’entrée d’une application est Startup, vous n’avez plus de dépendance relative à Global.asax. Quand vous employez OWIN avec le .NET Framework, utilisez l’exemple de code suivant en tant que pipeline :

using Owin;
using System.Web.Http;

namespace WebApi
{
    // Note: By default all requests go through this OWIN pipeline. Alternatively you can turn this off by adding an appSetting owin:AutomaticAppStartup with value “false”. 
    // With this turned off you can still have OWIN apps listening on specific routes by adding routes in global.asax file using MapOwinPath or MapOwinRoute extensions on RouteTable.Routes
    public class Startup
    {
        // Invoked once at startup to configure your application.
        public void Configuration(IAppBuilder builder)
        {
            HttpConfiguration config = new HttpConfiguration();
            config.Routes.MapHttpRoute("Default", "{controller}/{customerID}", new { controller = "Customer", customerID = RouteParameter.Optional });

            config.Formatters.XmlFormatter.UseXmlSerializer = true;
            config.Formatters.Remove(config.Formatters.JsonFormatter);
            // config.Formatters.JsonFormatter.UseDataContractJsonSerializer = true;

            builder.UseWebApi(config);
        }
    }
}

Cela permet de configurer vos itinéraires par défaut, et de privilégier XmlSerialization à JSON. Ajoutez d’autres intergiciels (middleware) à ce pipeline selon les besoins (services de chargement, paramètres de configuration, fichiers statiques, etc.).

ASP.NET Core utilise une approche similaire mais n’a pas besoin d’OWIN pour prendre en charge l’entrée. Au lieu de cela, la méthode Program.csMain est utilisée (similaire aux applications console) et Startup est chargé ainsi.

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace WebApplication2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

Startup doit inclure une méthode Configure. Dans Configure, ajoutez l’intergiciel (middleware) nécessaire au pipeline. Dans l’exemple suivant (provenant du modèle de site web par défaut), des méthodes d’extension configurent le pipeline pour une prise en charge des éléments ci-dessous :

  • Pages d’erreur
  • HTTP Strict Transport Security
  • Redirection HTTP vers HTTPS
  • ASP.NET Core MVC
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseMvc();
}

L’hôte et l’application ont été découplés, ce qui permet de passer facilement plus tard à une autre plateforme.

Remarque

Pour obtenir des informations de référence plus approfondies sur le démarrage des applications dans ASP.NET Core et sur les intergiciels (middleware), consultez Démarrage dans ASP.NET Core

Stocker les configurations

ASP.NET prend en charge le stockage des paramètres. Ces paramètres sont utilisés, par exemple, pour prendre en charge l’environnement sur lequel les applications ont été déployées. Habituellement, toutes les paires clé-valeur personnalisées sont stockées dans la section <appSettings> du fichier Web.config :

<appSettings>
  <add key="UserName" value="User" />
  <add key="Password" value="Password" />
</appSettings>

Les applications lisent ces paramètres à l’aide de la collection ConfigurationManager.AppSettings dans l’espace de noms System.Configuration :

string userName = System.Web.Configuration.ConfigurationManager.AppSettings["UserName"];
string password = System.Web.Configuration.ConfigurationManager.AppSettings["Password"];

ASP.NET Core peut stocker les données de configuration de l’application dans un fichier et les charger dans le cadre du démarrage d’un intergiciel (middleware). Le fichier par défaut utilisé dans les modèles de projet est appsettings.json :

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppConfiguration": {
    "UserName": "UserName",
    "Password": "Password"
  }
}

Le chargement de ce fichier dans une instance de IConfiguration à l’intérieur de votre application s’effectue dans Startup.cs :

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

L’application lit Configuration pour obtenir les paramètres :

string userName = Configuration.GetSection("AppConfiguration")["UserName"];
string password = Configuration.GetSection("AppConfiguration")["Password"];

Il existe des extensions à cette approche pour rendre le processus plus robuste, par exemple en utilisant l’injection de dépendances pour charger un service avec ces valeurs. L’approche par injection de dépendances fournit un ensemble d’objets de configuration fortement typés.

// Assume AppConfiguration is a class representing a strongly-typed version of AppConfiguration section
services.Configure<AppConfiguration>(Configuration.GetSection("AppConfiguration"));

Remarque

Pour obtenir des informations de référence plus approfondies sur la configuration d’ASP.NET Core, consultez Configuration dans ASP.NET Core.

Injection de dépendances native

Quand vous générez des applications majeures et scalables, il est important d’avoir un couplage faible entre les composants et les services. L’injection de dépendances est une technique répandue qui permet d’y parvenir. Elle représente un composant natif d’ASP.NET Core.

Dans les applications ASP.NET, les développeurs s’appuient sur une bibliothèque tierce pour implémenter l’injection de dépendances. L’une de ces bibliothèques, Unity, est fournie par Microsoft Patterns & Practices.

L’implémentation de IDependencyResolver qui inclut UnityContainer dans un wrapper est un exemple de configuration d’une injection de dépendances avec Unity :

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        container.Dispose();
    }
}

Créez une instance de UnityContainer, inscrivez votre service, puis définissez le programme de résolution de dépendances de HttpConfiguration en fonction de la nouvelle instance de UnityResolver pour votre conteneur :

public static void Register(HttpConfiguration config)
{
    var container = new UnityContainer();
    container.RegisterType<IProductRepository, ProductRepository>(new HierarchicalLifetimeManager());
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.
}

Injectez IProductRepository aux emplacements nécessaires :

public class ProductsController : ApiController
{
    private IProductRepository _repository;

    public ProductsController(IProductRepository repository)  
    {
        _repository = repository;
    }

    // Other controller methods not shown.
}

Étant donné que l’injection de dépendances fait partie d’ASP.NET Core, vous pouvez ajouter votre service à la méthode ConfigureServices de Startup.cs :

public void ConfigureServices(IServiceCollection services)
{
    // Add application services.
    services.AddTransient<IProductRepository, ProductRepository>();
}

Vous pouvez injecter le dépôt à l’emplacement de votre choix, comme c’était le cas avec Unity.

Remarque

Pour plus d’informations sur l’injection de dépendances, consultez Injection de dépendances.

Délivrer des fichiers statiques

Une partie importante du développement web réside dans la capacité de traitement des composants statiques, côté client. Les fichiers HTML, CSS, JavaScript et image sont les exemples les plus courants de fichiers statiques. Ces fichiers doivent être enregistrés à l’emplacement publié de l’application (ou CDN) et référencés pour pouvoir être chargés par une requête. Ce processus a changé avec ASP.NET Core.

Avec ASP.NET, les fichiers statiques sont stockés dans différents répertoires et référencés dans des vues.

Avec ASP.NET Core, les fichiers statiques sont stockés à la « racine web » (<racine du contenu>/wwwroot), sauf si la configuration est différente. Les fichiers sont chargés dans le pipeline de requêtes via l’appel de la méthode d’extension UseStaticFiles à partir de Startup.Configure :

Remarque

Si vous ciblez le .NET Framework, installez le package NuGet Microsoft.AspNetCore.StaticFiles.

Par exemple, un composant image dans le dossier wwwroot/images est accessible au navigateur à un emplacement tel que http://<app>/images/<imageFileName>.

Remarque

Pour obtenir des informations de référence plus approfondies sur le traitement des fichiers statiques dans ASP.NET Core, consultez Fichiers statiques.

cookie à valeurs multiples

Les cookie à valeurs multiples ne sont pas pris en charge dans ASP.NET Core. Créez un cookie par valeur.

Les cookies d’authentification ne sont pas compressés dans ASP.NET Core

Pour des raisons de sécurité, les cookies d’authentification ne sont pas compressés dans ASP.NET Core. Quand vous utilisez des cookies d’authentification, les développeurs doivent limiter les informations de revendication incluses à celles qui sont nécessaires selon leurs besoins.

Migration d’application partielle

L’une des approches de la migration d’applications partielle consiste à créer une sous-application IIS et à déplacer uniquement certains itinéraires d’ASP.NET 4.x vers ASP.NET Core, tout en conservant la structure d’URL de l’application. Tenez compte par exemple de la structure de l’URL de l’application dans le fichier applicationHost.config :

<sites>
    <site name="Default Web Site" id="1" serverAutoStart="true">
        <application path="/">
            <virtualDirectory path="/" physicalPath="D:\sites\MainSite\" />
        </application>
        <application path="/api" applicationPool="DefaultAppPool">
            <virtualDirectory path="/" physicalPath="D:\sites\netcoreapi" />
        </application>
        <bindings>
            <binding protocol="http" bindingInformation="*:80:" />
            <binding protocol="https" bindingInformation="*:443:" sslFlags="0" />
        </bindings>
    </site>
	...
</sites>

Structure de répertoires :

.
├── MainSite
│   ├── ...
│   └── Web.config
└── NetCoreApi
    ├── ...
    └── web.config

[BIND] et formateurs d’entrée

Les versions précédentes d’ASP.NET utilisaient l’attribut [Bind] pour fournir une protection contre les attaques par survalidation (« overposting »). Les formateurs d’entrée fonctionnent différemment dans ASP.NET Core. L’attribut [Bind] n’est plus conçu pour empêcher la survalidation (« overposting ») quand il est utilisé avec des formateurs d’entrée pour analyser JSON ou XML. Ces attributs affectent la liaison de données quand la source de données est une donnée de formulaire publiée avec le type de contenu x-www-form-urlencoded.

Pour les applications qui publient des informations JSON sur les contrôleurs et utilisent les formateurs d’entrée JSON pour analyser les données, nous vous recommandons de remplacer l’attribut [Bind] par un modèle de vue qui correspond aux propriétés définies par l’attribut [Bind].

Ressources supplémentaires