Nouveautés d’ASP.NET Core 3.0

Cet article met en évidence les modifications les plus importantes dans ASP.NET Core 3.0 et fournit des liens vers la documentation appropriée.

Blazor

Blazor est une nouvelle infrastructure dans ASP.NET Core pour la création d’une interface utilisateur web interactive côté client avec .NET :

  • Créer des interfaces utilisateur interactives riches en utilisant C#.
  • Partagez la logique d’application côté serveur et côté client écrite dans .NET.
  • Affichez l’interface utilisateur en langage HTML et CSS pour une large prise en charge des navigateurs, y compris les navigateurs mobiles.

Scénarios pris en charge par l’infrastructure Blazor :

  • Composants d’interface utilisateur réutilisables (composants Razor)
  • Routage côté client
  • Dispositions des composants
  • Prise en charge de l’injection de dépendances
  • Formulaires et validation
  • Fournir des composants Razor dans les bibliothèques de classes Razor
  • Interopérabilité JavaScript

Pour plus d’informations, consultez ASP.NET Core Blazor.

Blazor Server

Blazor dissocie la logique de rendu de composant de la manière dont les mises à jour de l’interface utilisateur sont appliquées. Blazor Server prend en charge l’hébergement des composants Razor sur le serveur dans une application ASP.NET Core. Les mises à jour de l’IU sont gérées via une connexion SignalR. Blazor Server est pris en charge dans ASP.NET Core 3.0.

Blazor WebAssembly (version préliminaire)

Les applications Blazor peuvent également être exécutées directement dans le navigateur à l’aide d’un runtime .NET basé sur WebAssembly. Blazor WebAssembly est en préversion et n’est pas pris en charge dans ASP.NET Core 3.0. Blazor WebAssembly sera pris en charge dans une prochaine version de ASP.NET Core.

Composants Razor

Les applications Blazor sont générées à partir de composants. Les composants sont des blocs autonomes d’interface utilisateur, tels qu’une page, une boîte de dialogue ou un formulaire. Les composants sont des classes .NET normales qui définissent la logique d’affichage de l’interface utilisateur et les gestionnaires d’événements côté client. Vous pouvez créer des applications web interactives riches sans JavaScript.

Les composants de Blazor sont généralement créés à l’aide de la syntaxe Razor, un mélange naturel de HTML et C#. Les composants Razor sont similaires à Razor Pages et MVC dans lesquelles ils utilisent tous les deux Razor. Contrairement aux pages et aux vues, qui sont basées sur un modèle de requête-réponse, les composants sont utilisés spécifiquement pour gérer la composition de l’interface utilisateur.

gRPC

gRPC :

  • Est une infrastructure RPC (appel de procédure distante) populaire et à hautes performance.

  • Offre une approche de contrat d’abord avisée pour le développement d’API.

  • Utilise des technologies modernes telles que :

    • HTTP/2 pour le transport.
    • Mémoires tampons de protocole comme langage de description de l’interface.
    • Format de sérialisation binaire.
  • Fournit des fonctionnalités telles que :

    • Authentification
    • Flux bidirectionnel et contrôle de flux.
    • Annulations et délais d’expiration.

La fonctionnalité gRPC dans ASP.NET Core 3.0 inclut :

  • Grpc.AspNetCore : infrastructure ASP.NET Core pour l’hébergement de services gRPC. gRPC sur ASP.NET Core s’intègre à des fonctionnalités standard ASP.NET Core telles que la journalisation, l’injection de dépendances (DI), l’authentification et l’autorisation.
  • Grpc.Net.Client : client gRPC pour .NET Core qui s’appuie sur le HttpClient familier.
  • Grpc.Net.ClientFactory : intégration du client gRPC à HttpClientFactory.

Pour plus d’informations, consultez Vue d’ensemble de gRPC sur .NET.

SignalR

Consultez le code de mise à jour SignalR pour obtenir des instructions de migration. SignalR utilise désormais System.Text.Json pour sérialiser/désérialiser les messages JSON. Consultez Basculer vers Newtonsoft.Json pour obtenir des instructions pour restaurer le sérialiseur basé sur Newtonsoft.Json.

Dans les clients JavaScript et .NET pour SignalR, la prise en charge a été ajoutée pour la reconnexion automatique. Par défaut, le client tente de se reconnecter immédiatement et réessaye après 2, 10 et 30 secondes si nécessaire. Si le client se reconnecte correctement, il reçoit un nouvel ID de connexion. La reconnexion automatique est activée :

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/chathub")
    .withAutomaticReconnect()
    .build();

Les intervalles de reconnexion peuvent être spécifiés en transmettant un tableau de durées en millisecondes :

.withAutomaticReconnect([0, 3000, 5000, 10000, 15000, 30000])
//.withAutomaticReconnect([0, 2000, 10000, 30000]) The default intervals.

Une implémentation personnalisée peut être transmise pour un contrôle total des intervalles de reconnexion.

Si la reconnexion échoue après le dernier intervalle de reconnexion :

  • Le client considère que la connexion est hors connexion.
  • Le client cesse de tenter de se reconnecter.

Pendant les tentatives de reconnexion, mettez à jour l’interface utilisateur de l’application pour informer l’utilisateur que la reconnexion est tentée.

Pour fournir des commentaires sur l’interface utilisateur lorsque la connexion est interrompue, l’API cliente SignalR a été développée pour inclure les gestionnaires d’événements suivants :

  • onreconnecting : permet aux développeurs de désactiver l’interface utilisateur ou de permettre aux utilisateurs de savoir que l’application est hors connexion.
  • onreconnected : permet aux développeurs de mettre à jour l’interface utilisateur une fois la connexion rétablie.

Le code suivant utilise onreconnecting pour mettre à jour l’interface utilisateur lors de la tentative de connexion :

connection.onreconnecting((error) => {
    const status = `Connection lost due to error "${error}". Reconnecting.`;
    document.getElementById("messageInput").disabled = true;
    document.getElementById("sendButton").disabled = true;
    document.getElementById("connectionStatus").innerText = status;
});

Le code suivant utilise onreconnected pour mettre à jour l’interface utilisateur sur la connexion :

connection.onreconnected((connectionId) => {
    const status = `Connection reestablished. Connected.`;
    document.getElementById("messageInput").disabled = false;
    document.getElementById("sendButton").disabled = false;
    document.getElementById("connectionStatus").innerText = status;
});

SignalR 3.0 et versions ultérieures fournit une ressource personnalisée aux gestionnaires d’autorisation lorsqu’une méthode hub nécessite une autorisation. La ressource est une instance de HubInvocationContext. HubInvocationContext inclut :

  • HubCallerContext
  • Le nom de la méthode hub appelée.
  • Les arguments de la méthode hub.

Prenons l’exemple suivant d’une application de conversation autorisant la connexion à plusieurs organisations via Azure Active Directory. Toute personne disposant d’un compte Microsoft peut se connecter à la conversation, mais seuls les membres de l’organisation propriétaire peuvent interdire les utilisateurs ou afficher les historiques de conversation des utilisateurs. L’application peut restreindre certaines fonctionnalités d’utilisateurs spécifiques.

public class DomainRestrictedRequirement :
    AuthorizationHandler<DomainRestrictedRequirement, HubInvocationContext>,
    IAuthorizationRequirement
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
        DomainRestrictedRequirement requirement,
        HubInvocationContext resource)
    {
        if (context.User?.Identity?.Name == null)
        {
            return Task.CompletedTask;
        }

        if (IsUserAllowedToDoThis(resource.HubMethodName, context.User.Identity.Name))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }

    private bool IsUserAllowedToDoThis(string hubMethodName, string currentUsername)
    {
        if (hubMethodName.Equals("banUser", StringComparison.OrdinalIgnoreCase))
        {
            return currentUsername.Equals("bob42@jabbr.net", StringComparison.OrdinalIgnoreCase);
        }

        return currentUsername.EndsWith("@jabbr.net", StringComparison.OrdinalIgnoreCase));
    }
}

Dans le code précédent, DomainRestrictedRequirement sert de IAuthorizationRequirement personnalisé. Étant donné que le paramètre de ressource HubInvocationContext est transmis, la logique interne peut :

  • Inspecter le contexte dans lequel le hub est appelé.
  • Prendre des décisions sur l’autorisation de l’utilisateur d’exécuter des méthodes Hub individuelles.

Les méthodes hub individuelles peuvent être marquées avec le nom de la stratégie que le code vérifie au moment de l’exécution. Lorsque les clients tentent d’appeler des méthodes Hub individuelles, le gestionnaire DomainRestrictedRequirement s’exécute et contrôle l’accès aux méthodes. En fonction de la façon dont les contrôles DomainRestrictedRequirement accèdent :

  • Tous les utilisateurs connectés peuvent appeler la méthode SendMessage.
  • Seuls les utilisateurs qui se sont connectés avec une adresse e-mail @jabbr.net peuvent afficher les historiques des utilisateurs.
  • Seul bob42@jabbr.net peut interdire les utilisateurs de la salle de conversation.
[Authorize]
public class ChatHub : Hub
{
    public void SendMessage(string message)
    {
    }

    [Authorize("DomainRestricted")]
    public void BanUser(string username)
    {
    }

    [Authorize("DomainRestricted")]
    public void ViewUserHistory(string username)
    {
    }
}

La création de la stratégie DomainRestricted peut impliquer :

  • Dans Startup.cs, ajout de la nouvelle stratégie.
  • Fournir l’exigence personnalisée DomainRestrictedRequirement en tant que paramètre.
  • Inscription de DomainRestricted auprès de l’intergiciel d’autorisation.
services
    .AddAuthorization(options =>
    {
        options.AddPolicy("DomainRestricted", policy =>
        {
            policy.Requirements.Add(new DomainRestrictedRequirement());
        });
    });

Les hubs SignalR utilisent le routage des points de terminaison. La connexion hub SignalR a été effectuée explicitement :

app.UseSignalR(routes =>
{
    routes.MapHub<ChatHub>("hubs/chat");
});

Dans la version précédente, les développeurs devaient connecter des contrôleurs, des pages Razor et des hubs à divers endroits. Une connexion explicite entraîne une série de segments de routage presque identiques :

app.UseSignalR(routes =>
{
    routes.MapHub<ChatHub>("hubs/chat");
});

app.UseRouting(routes =>
{
    routes.MapRazorPages();
});

Les hubs SignalR 3.0 peuvent être routés via le routage du point de terminaison. Avec le routage des points de terminaison, tous les routages peuvent généralement être configurés dans UseRouting :

app.UseRouting(routes =>
{
    routes.MapRazorPages();
    routes.MapHub<ChatHub>("hubs/chat");
});

SignalR ASP.NET Core 3.0 ajouté :

Flux client à serveur. Avec le flux client à serveur, les méthodes côté serveur peuvent prendre des instances de IAsyncEnumerable<T> ou ChannelReader<T>. Dans l’exemple C# suivant, la méthode UploadStream sur le hub reçoit un flux de chaînes du client :

public async Task UploadStream(IAsyncEnumerable<string> stream)
{
    await foreach (var item in stream)
    {
        // process content
    }
}

Les applications clientes .NET peuvent passer une instance IAsyncEnumerable<T> ou ChannelReader<T> en tant qu’argument stream de la méthode Hub UploadStream ci-dessus.

Une fois la boucle for terminée et que la fonction locale se termine, l’achèvement du flux est envoyé :

async IAsyncEnumerable<string> clientStreamData()
{
    for (var i = 0; i < 5; i++)
    {
        var data = await FetchSomeData();
        yield return data;
    }
}

await connection.SendAsync("UploadStream", clientStreamData());

Les applications clientes JavaScript utilisent SignalRSubject (ou un objet RxJS) pour l’argument stream de la méthode Hub UploadStream ci-dessus.

let subject = new signalR.Subject();
await connection.send("StartStream", "MyAsciiArtStream", subject);

Le code JavaScript peut utiliser la méthode subject.next pour gérer les chaînes à mesure qu’elles sont capturées et prêtes à être envoyées au serveur.

subject.next("example");
subject.complete();

À l’aide de code comme les deux extraits précédents, des expériences de flux en temps réel peuvent être créées.

Nouvelle sérialisation JSON

ASP.NET Core 3.0 utilise désormais System.Text.Json par défaut pour la sérialisation JSON :

  • Lit et écrit JSON de manière asynchrone.
  • Est optimisé pour le texte UTF-8.
  • En général, les performances sont plus élevées que Newtonsoft.Json.

Pour ajouter Json.NET à ASP.NET Core 3.0, consultez Ajouter la prise en charge du format JSON basé sur Newtonsoft.Json.

Nouvelles directives Razor

La liste suivante contient de nouvelles directives Razor :

  • @attribute: la directive @attribute applique l’attribut donné à la classe de la page ou de la vue générée. Par exemple : @attribute [Authorize].
  • @implements: la directive @implements implémente une interface pour la classe générée. Par exemple : @implements IDisposable.

IdentityServer4 prend en charge l’authentification et l’autorisation pour les API web et les SPA

ASP.NET Core 3.0 offre une authentification dans Single Page Apps (SPA) à l’aide de la prise en charge de l’autorisation de l’API web. ASP.NET Core Identity pour l’authentification et le stockage des utilisateurs est combiné avec IdentityServer4 pour implémenter OpenID Connect.

IdentityServer4 est une infrastructure OpenID Connect et OAuth 2.0 pour ASP.NET Core 3.0. Il active les fonctionnalités de sécurité suivantes :

  • Authentification en tant que service (AaaS)
  • Authentification/déconnexion unique (SSO) sur plusieurs types d’applications
  • Contrôle d’accès pour les API
  • Federation Gateway

Pour plus d’informations, consultez la documentation IdentityServer4 ou Authentification et autorisation pour les SPA.

Authentification par certificat et Kerberos

L’authentification par certificat nécessite :

  • La configuration du serveur pour accepter des certificats.
  • L’ajout de l’intergiciel d’authentification dans Startup.Configure.
  • L’ajout du service d’authentification de certificat dans Startup.ConfigureServices.
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
            .AddCertificate();
    // Other service configuration removed.
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
    // Other app configuration removed.
}

Les options d’authentification par certificat incluent la possibilité de :

  • Accepter les certificats auto-signés.
  • Vérifier la révocation des certificats.
  • Vérifier que le certificat présenté a les indicateurs d’utilisation appropriés.

Un principal d’utilisateur par défaut est construit à partir des propriétés du certificat. Le principal d’utilisateur contient un événement qui permet de compléter ou de remplacer le principal. Pour plus d’informations, consultez Configurer l’authentification par certificat dans ASP.NET Core.

L’authentification Windows a été étendue sur Linux et macOS. Dans les versions précédentes, l’authentification Windows était limitée à IIS et HTTP.sys. Dans ASP.NET Core 3.0, Kestrel dispose de la possibilité d’utiliser Negotiate, Kerberoset NTLM sur Windows, Linux et macOS pour les hôtes joints à un domaine Windows. La prise en charge de ces schémas d’authentification par Kestrel est fournie par le package NuGet Microsoft.AspNetCore.Authentication.Negotiate NuGet . Comme avec les autres services d’authentification, configurez l’authentification à l’échelle de l’application, puis configurez le service :

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
        .AddNegotiate();
    // Other service configuration removed.
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
    // Other app configuration removed.
}

Configuration requise pour l’hôte :

  • Les hôtes Windows doivent avoir des noms de principal du service (SPN) ajoutés au compte d’utilisateur hébergeant l’application.
  • Les machines Linux et macOS doivent être jointes au domaine.
    • Les SPN doivent être créés pour le processus web.
    • Les fichiers Keytab doivent être générés et configurés sur l’ordinateur hôte.

Pour plus d’informations, consultez Configurer l’authentification Windows par certificat dans ASP.NET Core.

Modifications de modèle

Les éléments des modèles d’interface utilisateur web (Razor Pages, MVC avec contrôleur et vues) suivants ont été supprimés :

Le modèle Angular a été mis à jour pour utiliser Angular 8.

Le modèle de bibliothèque de classes Razor (RCL) est défini par défaut sur développement de composants Razor. Une nouvelle option de modèle dans Visual Studio fournit la prise en charge des modèles pour les pages et les vues. Lors de la création d’une liste RCL à partir du modèle dans une interface de commandes, passez l’option --support-pages-and-views (dotnet new razorclasslib --support-pages-and-views).

Hôte générique

Les modèles ASP.NET Core 3.0 utilisent l’hôte générique .NET dans ASP.NET Core. Les versions précédentes utilisaient WebHostBuilder. L’utilisation de l’hôte générique .NET Core (HostBuilder) offre une meilleure intégration des applications ASP.NET Core avec d’autres scénarios de serveur qui ne sont pas spécifiques au web. Pour plus d’informations, consultez HostBuilder remplace WebHostBuilder.

Configuration de l’hôte

Avant la publication de ASP.NET Core 3.0, les variables d’environnement préfixées avec ASPNETCORE_ étaient chargées pour la configuration de l’hôte web. Dans la version 3.0, AddEnvironmentVariables est utilisé pour charger des variables d’environnement précédées de DOTNET_ pour la configuration de l’hôte avec CreateDefaultBuilder.

Modifications apportées à l’injection de constructeur de démarrage

L’hôte générique prend uniquement en charge les types suivants pour l’injection de constructeur Startup :

Tous les services peuvent toujours être injectés directement en tant qu’arguments à la méthode Startup.Configure. Pour plus d’informations, consultez L’hôte générique limite l’injection de constructeur de démarrage (aspnet/Announcements #353).

Kestrel

  • La configuration de Kestrel a été mise à jour pour la migration vers l’hôte générique. Dans la version 3.0, Kestrel est configuré sur le générateur d’hôtes web fourni par ConfigureWebHostDefaults.
  • Les adaptateurs de connexion ont été supprimés de Kestrel et remplacés par l’intergiciel de connexion, qui est similaire à l’intergiciel HTTP dans le pipeline ASP.NET Core, mais pour les connexions de niveau inférieur.
  • La couche de transport Kestrel a été exposée en tant qu’interface publique dans Connections.Abstractions.
  • L’ambiguïté entre les en-têtes et les codes de fin a été résolue en déplaçant les en-têtes de fin vers une nouvelle collection.
  • Les API d’E/S synchrones, telles que HttpRequest.Body.Read, sont une source courante de privation de threads entraînant des incidents d’applications. Dans la version 3.0, AllowSynchronousIO est désactivé par défaut.

Pour plus d’informations, consultez Migrer de ASP.NET Core 2.2 vers 3.0.

HTTP/2 activé par défaut

HTTP/2 est activé par défaut dans Kestrel pour les points de terminaison HTTPS. La prise en charge de HTTP/2 pour IIS ou HTTP.sys est activée lorsqu’elle est prise en charge par le système d’exploitation.

EventCounters à la demande

L’hôte EventSource, Microsoft.AspNetCore.Hosting, émet les nouveaux types EventCounter suivants liés aux requêtes entrantes :

  • requests-per-second
  • total-requests
  • current-requests
  • failed-requests

Routage de point de terminaison

Le routage des points de terminaison, qui permet aux frameworks (par exemple, MVC) de fonctionner correctement avec le middleware, est amélioré :

  • L’ordre des intergiciels et des points de terminaison est configurable dans le pipeline de traitement des demandes de Startup.Configure.
  • Les points de terminaison et les intergiciels composent bien avec d’autres technologies ASP.NET Core, telles que les contrôles d’intégrité.
  • Les points de terminaison peuvent implémenter une stratégie, telle que CORS ou autorisation, dans les intergiciels et MVC.
  • Les filtres et les attributs peuvent être placés sur des méthodes dans des contrôleurs.

Pour plus d’informations, consultez Routage dans ASP.NET Core.

Contrôles d'intégrité

Les contrôles d’intégrité utilisent le routage des points de terminaison avec l’hôte générique. Dans Startup.Configure, appelez MapHealthChecks sur le générateur de points de terminaison avec l’URL du point de terminaison ou le chemin relatif :

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
});

Les points de terminaison de contrôle d’intégrité peuvent :

  • Spécifier un ou plusieurs hôtes/ports autorisés.
  • Exiger une autorisation.
  • Exiger CORS.

Pour plus d’informations, consultez les articles suivants :

Canaux sur HttpContext

Il est désormais possible de lire le corps de la requête et d’écrire le corps de la réponse à l’aide de l’API System.IO.Pipelines. La propriété HttpRequest.BodyReader fournit PipeReader qui peut être utilisé pour lire le corps de la requête. La propriété HttpResponse.BodyWriter fournit PipeWriter qui peut être utilisé pour écrire le corps de la réponse. HttpRequest.BodyReader est un analogue du flux HttpRequest.Body. HttpResponse.BodyWriter est un analogue du flux HttpResponse.Body.

Amélioration du rapport d’erreurs dans IIS

Les erreurs de démarrage lors de l’hébergement d’applications ASP.NET Core dans IIS produisent désormais des données de diagnostic plus riches. Ces erreurs sont signalées au journal des événements Windows avec des rapports des appels de procédure, le cas échéant. En outre, tous les avertissements, erreurs et exceptions non gérées sont enregistrés dans le journal des événements Windows.

Kit de développement logiciel (SDK) Worker Service and Worker

.NET Core 3.0 introduit le nouveau modèle d’application Worker Service. Ce modèle fournit un point de départ pour l’écriture de services durables dans .NET Core.

Pour plus d'informations, consultez les pages suivantes :

Améliorations apportées aux intergiciels d’en-têtes transférés

Dans les versions précédentes de ASP.NET Core, l’appel de UseHsts et UseHttpsRedirection étaient problématiques lors du déploiement sur Linux Azure ou derrière un proxy inverse autre que IIS. Le correctif pour les versions précédentes est documenté dans Transférer le schéma pour les proxys inverses Linux et non IIS.

Ce scénario est résolu dans ASP.NET Core 3.0. L’hôte active le middleware des en-têtes transférés lorsque la variable d’environnement ASPNETCORE_FORWARDEDHEADERS_ENABLED est définie sur true. ASPNETCORE_FORWARDEDHEADERS_ENABLED est défini true sur nos images conteneur.

Optimisation des performances

ASP.NET Core 3.0 inclut de nombreuses améliorations qui réduisent l’utilisation de la mémoire et améliorent le débit :

  • Réduction de l’utilisation de la mémoire lors de l’utilisation du conteneur d’injection de dépendances intégré pour les services étendus.
  • Réduction des répartitions dans l’infrastructure, y compris les scénarios d’intergiciel et le routage.
  • Réduction de l’utilisation de la mémoire pour les connexions WebSocket.
  • Améliorations de la réduction de la mémoire et du débit pour les connexions HTTPS.
  • Nouveau sérialiseur JSON optimisé et entièrement asynchrone.
  • Réduction de l’utilisation de la mémoire et des améliorations du débit dans l’analyse de formulaire.

ASP.NET Core 3.0 s’exécute uniquement sur .NET Core 3.0

Depuis ASP.NET Core 3.0, .NET Framework n’est plus un framework cible pris en charge. Les projets ciblant .NET Framework peuvent continuer de manière entièrement prise en charge à l’aide de la version LTS .NET Core 2.1. La plupart des packages associés à Core 2.1.x ASP.NET seront pris en charge indéfiniment, au-delà de la période LTS de trois ans pour .NET Core 2.1.

Pour plus d’informations sur la migration, consultez Port de votre code .NET Framework vers .NET Core.

Utiliser l’infrastructure partagée ASP.NET Core

L’infrastructure partagée ASP.NET Core 3.0, contenue dans le métapaquet Microsoft.AspNetCore.App, ne nécessite plus d’élément explicite <PackageReference /> dans le fichier projet. L’infrastructure partagée est automatiquement référencée lors de l’utilisation du Kit de développement logiciel (SDK) Microsoft.NET.Sdk.Web dans le fichier projet :

<Project Sdk="Microsoft.NET.Sdk.Web">

Assembly supprimés de l’infrastructure partagée ASP.NET Core

Les assembly les plus notables supprimés de l’infrastructure partagée ASP.NET Core 3.0 sont les suivants :

Pour obtenir la liste complète des assembly supprimés de l’infrastructure partagée, consultez Assembly supprimés de Microsoft.AspNetCore.App 3.0. Pour plus d’informations sur la motivation de ce changement, consultez Changements cassants de Microsoft.AspNetCore.App dans la version 3.0 et Premier aperçu des modifications apportées à ASP.NET Core 3.0.