Partager via


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 à ASP.NET Core.

Pour les applications ASP.NET MVC et Web API, consultez Apprenez à passer de ASP.NET MVC et Web API à ASP.NET Core MVC. Pour les applications Web Forms ASP.NET Framework, consultez Apprenez à effectuer la mise à niveau de ASP.NET Web Forms vers ASP.NET Core.

Modèles d’application web d’entreprise

Pour obtenir des conseils sur la création d’une application ASP.NET Core fiable, sécurisée, performante, performante et évolutive, consultez les modèles d’application web d’entreprise. Un exemple complet d’application web de qualité de production qui implémente les modèles est disponible.

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 Encodé ASP.NET Core Infrastructure de ASP.NET
\ %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 de ASP.NET vers ASP.NET Core dans Visual Studio.

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

Pour un guide de portage complet, consultez l'ebook Porting existing ASP.NET apps to .NET Core.

Prérequis

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 Choisir entre .NET Core et .NET Framework pour les applications serveur afin de déterminer le framework cible le plus approprié.

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

Le ciblage de .NET Core vous permet d’éliminer de nombreuses références de package 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 Microsoft.AspNetCore.App métapackage 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 :

    Modifier l’option de menu contextuel CSPROJ dans 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 pour les applications ASP.NET est le fichier Global.asax . Les tâches telles que la configuration de routage et les inscriptions de filtre et de zone sont géré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. Dans un effort de découplage, OWIN a été introduit pour fournir un moyen plus propre d’utiliser plusieurs frameworks ensemble. OWIN fournit un pipeline qui permet d’ajouter uniquement les modules nécessaires. L’environnement d’hébergement prend une fonction de démarrage pour configurer les services et le pipeline de requête 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, le point d’entrée d’une application est Startup, et vous n’avez plus de dépendance sur 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. À la place, cela se fait au travers de la méthode Program.csMain (similaire aux applications console) et Startup y est chargé.

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
  • Sécurité du transport HTTP Strict
  • 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 une référence plus approfondie à ASP.NET Core Startup and Middleware, consultez Startup in 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. Une pratique courante était de stocker toutes les paires clé-valeur personnalisées dans la <appSettings> section 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, comme l’utilisation de l’injection de dépendances (DI) 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 une référence plus approfondie à la configuration 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 populaire pour atteindre cela, et il s’agit d’un composant natif de 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 est Unity, 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.

Dans ASP.NET Core, les fichiers statiques sont stockés dans la « racine web » (<racine> de contenu/wwwroot), sauf configuration contraire. 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, une ressource d’image dans le dossier wwwroot/images est accessible au navigateur à un emplacement tel que http://<app>/images/<imageFileName>.

Remarque

Pour obtenir une référence plus approfondie au service de fichiers statiques dans ASP.NET Core, consultez Fichiers statiques.

Cookies multi-valeurs

Les cookies à 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. Lors de l’utilisation 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. Par exemple, considérez la structure d’URL de l’application à partir du 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 de ASP.NET utilisaient l’attribut [Bind] pour se protéger contre les attaques de surpostage. 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