Nouveautés dans ASP.NET Core 6.0

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

Améliorations MVC et Razor dans ASP.NET Core

API minimales

Les API minimales sont conçues pour créer des API HTTP ayant des dépendances minimales. Elles sont idéales pour les microservices et les applications qui ne nécessitent qu’un minimum de fichiers, de fonctionnalités et de dépendances dans ASP.NET Core. Pour plus d'informations, consultez les pages suivantes :

SignalR

Balise d’activité durable pour les connexions SignalR

SignalR utilise le nouveau Microsoft.AspNetCore.Http.Features.IHttpActivityFeature.Activity pour ajouter une balise http.long_running à l’activité de requête. IHttpActivityFeature.Activity est utilisé par les services APM comme Azure Monitor Application Insights pour filtrer les requêtes SignalR de la création d’alertes de requête durables.

SignalRAmélioration des performances

Compilateur Razor

Compilateur Razor mis à jour pour utiliser des générateurs sources

Le compilateur Razor est désormais basé sur des générateurs sources C#. Les générateurs sources s’exécutent durant la compilation et inspectent les éléments compilés afin de produire des fichiers supplémentaires qui sont compilés avec le reste du projet. L’utilisation de générateurs sources simplifie le compilateur Razor et accélère considérablement la durée de génération.

Le compilateur Razor ne produit plus un assembly d’affichages distinct

Le compilateur Razor a précédemment utilisé un processus de compilation en deux étapes qui a produit un assembly d’affichages distinct contenant les vues et les pages générées (fichiers .cshtml) définies dans l’application. Les types générés étaient publics et sous l’espace de noms AspNetCore.

Le compilateur Razor mis à jour génère les types de vues et de pages dans l’assembly du projet principal. Ces types sont désormais générés par défaut en tant que types internes sealed dans l’espace de noms AspNetCoreGeneratedDocument. Cette modification améliore les performances de génération, permet le déploiement de fichiers uniques et permet à ces types de participer au Rechargement à chaud.

Pour plus d’informations sur cette modification, consultez le problème des annonces associé sur GitHub.

Améliorations du niveau de performance et des API sur ASP.NET Core

De nombreuses modifications ont été réalisées pour réduire les allocations et améliorer le niveau de performance sur l’ensemble de la pile :

Empreinte mémoire réduite pour les connexions TLS inactives

Pour les connexions TLS durables où les données ne sont envoyées que de temps en temps dans les deux sens, nous avons considérablement réduit l’empreinte mémoire des applications ASP.NET Core dans .NET 6. Cela devrait permettre d’améliorer la scalabilité des scénarios tels que les serveurs WebSocket. Cela a été possible en raison des nombreuses améliorations apportées à System.IO.Pipelines, SslStream et Kestrel. Les sections suivantes présentent en détail quelques-unes des améliorations qui ont contribué à réduire l’empreinte mémoire :

Réduire la taille de System.IO.Pipelines.Pipe

Pour chaque connexion établie, deux canaux sont alloués dans Kestrel :

  • La couche de transport vers l’application de la requête.
  • La couche d’application vers le transport de la réponse.

En réduisant la taille de System.IO.Pipelines.Pipe de 368 octets à 264 octets (une réduction d’environ 28,2 %), il est possible d’économiser 208 octets par connexion (104 octets par canal).

Pool SocketSender

Les objets SocketSender (de la sous-classe SocketAsyncEventArgs) sont d’environ 350 octets au moment de l’exécution. Au lieu d’allouer un nouvel objet SocketSender par connexion, il est possible de les regrouper. Les objets SocketSender peuvent être regroupés, car les envois sont généralement très rapides. Le regroupement réduit la surcharge par connexion. Au lieu d’allouer 350 octets par connexion, ne payez que les 350 octets qui sont alloués par IOQueue. L’allocation est effectuée par file d’attente pour éviter tout conflit. Notre serveur WebSocket avec 5 000 connexions inactives est passé d’une allocation d’environ 1,75 Mo (350 octets * 5 000) à une allocation d’environ 2,8 Ko (350 octets * 8) pour les objets SocketSender.

Lectures à zéro octet avec SslStream

Les lectures sans mémoire tampon sont une technique utilisée dans ASP.NET Core pour éviter de louer la mémoire du pool de mémoires lorsqu’il n’y a aucune donnée disponible sur le socket. Avant cette modification, notre serveur WebSocket à 5 000 connexions inactives avait besoin d’environ 200 Mo sans TLS, contre environ 800 Mo avec TLS. Certaines de ces allocations (4 000 par connexion) étaient dues à Kestrel qui devait conserver une mémoire tampon ArrayPool<T> en attendant que les lectures sur SslStream se terminent. Étant donné que ces connexions étaient inactives, aucune des lectures ne s’est terminée et n’a renvoyé ses mémoires tampons au ArrayPool, ce qui a forcé ArrayPool à allouer plus de mémoire. Les allocations restantes étaient dans le SslStream : une mémoire tampon de 4 000 pour l’établissement des liaisons TLS et une mémoire tampon de 32 000 pour les lectures normales. Dans .NET 6, lorsque l’utilisateur effectue une lecture à zéro octet sur SslStream et qu’il n’a aucune donnée disponible, SslStream effectue en interne une lecture à zéro octet sur le flux sous-jacent inclus dans un wrapper. Dans le meilleur cas (connexion inactive), ces modifications permettent d’économiser 40 Ko par connexion tout en permettant au consommateur (Kestrel) d’être averti lorsque des données sont disponibles sans conserver de mémoires tampons inutilisées.

Lectures à zéro octet avec PipeReader

Les lectures sans mémoire tampon étant prises en charge sur SslStream, une option pour réaliser des lectures à zéro octet a été ajoutée à StreamPipeReader, le type interne qui adapte un Stream dans un PipeReader. Dans Kestrel, un StreamPipeReader est utilisé pour adapter le SslStream sous-jacent à un PipeReader. Par conséquent, il était nécessaire d’exposer ces sémantiques de lecture à zéro octet sur le PipeReader.

Dorénavant, un PipeReader prenant en charge les lectures à zéro octet sur n’importe quel Stream sous-jacent qui prend en charge la sémantique de lecture à zéro octet (par exemple SslStream,NetworkStream, etc.) peut être créé à l’aide de l’API suivante :

var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(useZeroByteReads: true));

Supprimer des sections du SlabMemoryPool

Pour réduire la fragmentation du tas, Kestrel a utilisé une technique selon laquelle il a alloué des sections de mémoire de 128 Ko dans le cadre de son pool de mémoires. Les sections étaient ensuite divisées en blocs de 4 Ko qui étaient utilisés par Kestrel en interne. Les sections devaient être supérieures à 85 Ko pour forcer l’allocation sur le tas d’objets volumineux afin d’essayer d’empêcher le GC de déplacer ce groupe. Toutefois, avec l’introduction du GC de nouvelle génération, le tas d’objets épinglés (POH), il n’est plus logique d’allouer des blocs sur la section. Kestrel alloue désormais des blocs directement sur le POH, ce qui réduit la complexité de la gestion du pool de mémoires. Cette modification devrait faciliter l’exécution d’améliorations ultérieures, telles que la réduction du pool de mémoires utilisé par Kestrel.

IAsyncDisposable pris en charge

IAsyncDisposable est désormais disponible pour les contrôleurs, Razor Pages et les composants d’affichage. Des versions asynchrones ont été ajoutées aux interfaces pertinentes dans les fabriques et les activateurs :

  • Les nouvelles méthodes offrent une implémentation d’interface par défaut qui délègue à la version synchrone et appelle Dispose.
  • Les implémentations remplacent l’implémentation par défaut et gèrent les implémentations IAsyncDisposable de suppression.
  • Les implémentations favorisent IAsyncDisposable sur IDisposable lorsque les deux interfaces sont implémentées.
  • Les extendeurs doivent remplacer les nouvelles méthodes incluses pour prendre en charge les instances IAsyncDisposable.

IAsyncDisposable est utile lors de l’utilisation de :

  • Énumérateurs asynchrones, par exemple, dans des flux asynchrones.
  • Ressources non gérées ayant des opérations d’E/S nécessitant beaucoup de ressources à mettre en production.

Lors de l’implémentation de cette interface, utilisez la méthode DisposeAsync pour mettre des ressources en production.

Envisagez d’utiliser un contrôleur qui crée et utilise un Utf8JsonWriter. Utf8JsonWriter est une ressource IAsyncDisposable :

public class HomeController : Controller, IAsyncDisposable
{
    private Utf8JsonWriter? _jsonWriter;
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
        _jsonWriter = new Utf8JsonWriter(new MemoryStream());
    }

IAsyncDisposable doit implémenter DisposeAsync :

public async ValueTask DisposeAsync()
{
    if (_jsonWriter is not null)
    {
        await _jsonWriter.DisposeAsync();
    }

    _jsonWriter = null;
}

Port Vcpkg pour le client C++ sur SignalR

Vcpkg est un gestionnaire de package de ligne de commande multiplateforme pour les bibliothèques C et C++. Nous avons récemment ajouté un port vers vcpkg pour ajouter la prise en charge native CMake du client C++ sur SignalR. vcpkg fonctionne également avec MSBuild.

Le client SignalR peut être ajouté à un projet CMake avec l’extrait de code suivant lorsque le vcpkg est inclus dans le fichier de chaîne d’outils :

find_package(microsoft-signalr CONFIG REQUIRED)
link_libraries(microsoft-signalr::microsoft-signalr)

Avec l’extrait de code précédent, le client C++ sur SignalR est prêt à utiliser #include et à être utilisé dans un projet sans configuration supplémentaire. Pour obtenir un exemple complet d’une application C++ qui utilise le client C++ sur SignalR, consultez le référentiel halter73/SignalR-Client-Cpp-Sample.

Blazor

Changements au modèle de projet

Plusieurs changements ont été apportés au modèle de projet pour les applications Blazor, notamment l’utilisation du fichier Pages/_Layout.cshtml pour le contenu de layout qui est apparu dans le fichier _Host.cshtml des applications Blazor Server antérieures. Étudiez les changements en créant une application à partir d’un modèle de projet 6.0 ou en accédant à la source de référence ASP.NET Core pour les modèles de projet :

Prise en charge des dépendances Blazor WebAssembly natives

Les applications Blazor WebAssembly peuvent utiliser des dépendances natives créées pour s’exécuter sur WebAssembly. Pour plus d’informations, consultez Dépendances natives ASP.NET Core Blazor WebAssembly.

Compilation Ahead Of Time (AOT) WebAssembly et nouvelle liaison du runtime

Blazor WebAssembly prend en charge la compilation Ahead Of Time (AOT) qui vous permet de compiler votre code .NET directement dans WebAssembly. La compilation AOT permet d’améliorer les performances du runtime au détriment d’une plus grande taille d’application. La nouvelle liaison du runtime WebAssembly .NET réduit le code d’exécution inutilisé et améliore ainsi la vitesse de téléchargement. Pour plus d’informations, consultez Compilation Ahead Of Time (AOT) et Nouvelle liaison du runtime.

Conserver l’état prérendu

Blazor prend en charge l’état persistant dans une page prérendue pour qu’il ne soit pas nécessaire de recréer l’état lorsque l’application est entièrement chargée. Pour plus d’informations, consultez Prérendu et intégration des composants ASP.NET Core Razor.

Limites d’erreur

Les limites d’erreur fournissent une approche pratique pour la gestion des exceptions au niveau de l’interface utilisateur. Pour plus d’informations, consultez Gérer les erreurs dans les applications ASP.NET Core Blazor.

Prise en charge de SVG

L’élément <foreignObject> element est pris en charge pour afficher du code HTML arbitraire dans un SVG. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Prise en charge Blazor Server du transfert de tableau d’octets dans l’interopérabilité JS

Blazor prend en charge l’interopérabilité JS des tableaux d’octets optimisés, qui évite l’encodage et le décodage des tableaux d’octets en Base64. Pour plus d’informations, consultez les ressources suivantes :

Améliorations de la chaîne de requête

La prise en charge de l’utilisation des chaînes de requête a été améliorée. Pour plus d’informations, consultez Routage et navigation ASP.NET Core Blazor.

Liaison pour la sélection multiple

La liaison prend en charge la sélection de plusieurs options avec des éléments <input>. Pour plus d’informations, consultez les ressources suivantes :

Contrôle de contenu d’en-tête (<head>)

Les composants Razor peuvent modifier le contenu de l’élément HTML <head> d’une page, notamment la définition du titre de la page (élément <title>) et la modification des métadonnées (éléments <meta>). Pour plus d’informations, consultez Contrôle du contenu <head> dans les applications ASP.NET Core Blazor.

Générer des composants Angular et React

Générez des composants JavaScript spécifiques à l’infrastructure à partir de composants Razor pour les infrastructures web, comme Angular ou React. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Générer le rendu de composants à partir de JavaScript

Générer le rendu de composants Razor dynamiquement à partir de JavaScript pour les applications JavaScript existantes. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Éléments personnalisés

La prise en charge expérimentale est disponible pour la création d’éléments personnalisés qui utilisent des interfaces HTML standard. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Déduire les types génériques des composants à partir de composants ancêtres

Un composant ancêtre peut mettre en cascade un paramètre de type par nom à des descendants à l’aide du nouvel attribut [CascadingTypeParameter]. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Composants rendus dynamiquement

Utilisez le nouveau composant DynamicComponent intégré pour générer le rendu des composants par type. Pour plus d’informations, consultez Composants ASP.NET Core Razor rendus dynamiquement.

Accessibilité Blazor améliorée

Utilisez le nouveau composant FocusOnNavigate pour définir le focus de l’interface utilisateur sur un élément basé sur un sélecteur CSS après avoir navigué d’une page à l’autre. Pour plus d’informations, consultez Routage et navigation ASP.NET Core Blazor.

Prise en charge d’arguments d’événements personnalisés

Blazor prend en charge les arguments d’événements personnalisés, ce qui vous permet de transmettre des données arbitraires aux gestionnaires d’événements .NET avec des événements personnalisés. Pour plus d’informations, consultez Gestion des événements ASP.NET CoreBlazor.

Paramètres obligatoires

Appliquez le nouvel attribut [EditorRequired] pour spécifier un paramètre de composant obligatoire. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Collocation de fichiers JavaScript avec des pages, des vues et des composants

La collocation de fichiers JavaScript pour les pages, les vues et les composants Razor est un moyen pratique d’organiser les scripts dans une application. Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

Initialiseurs JavaScript

Les initialiseurs JavaScript exécutent la logique avant et après le chargement d’une application Blazor. Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

Interopérabilité de JavaScript lors de la diffusion en continu

Blazor prend désormais en charge la diffusion en continu de données directement entre .NET et JavaScript. Pour plus d’informations, consultez les ressources suivantes :

Contraintes de type générique

Les paramètres de type générique sont désormais pris en charge. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Disposition du déploiement WebAssembly

Utilisez une disposition de déploiement pour activer les téléchargements d’applications Blazor WebAssembly dans des environnements à sécurité restreinte. Pour plus d’informations, consultez Disposition de déploiement pour les applications Blazor WebAssembly hébergées sur ASP.NET Core.

Nouveaux articles Blazor

Outre les fonctionnalités de Blazor décrites dans les sections précédentes, de nouveaux articles Blazor sont disponibles sur les sujets suivants :

Création d’applications Blazor Hybrid avec .NET MAUI, WPF et Windows Forms

Utilisez Blazor Hybrid pour combiner des infrastructures clientes natives de bureau et mobiles avec .NET et Blazor :

  • .NET Multi-platform App UI (.NET MAUI) : est une infrastructure multiplateforme pour la création d’applications mobiles et de bureau natives en C# et XAML.
  • Les applications Blazor Hybrid peuvent être générées avec les infrastructures Windows Presentation Foundation (WPF) et Windows Forms.

Important

Blazor Hybrid est en préversion et ne doit pas être utilisé dans les applications de production avant la version finale.

Pour plus d’informations, consultez les ressources suivantes :

Kestrel

HTTP/3 est actuellement en état de brouillon et peut donc faire l’objet de modifications. La prise en charge du protocole HTTP/3 dans ASP.NET Core n’est pas publiée, il s’agit d’une fonctionnalité d’évaluation incluse dans .NET 6.

Kestrel prend désormais en charge le protocole HTTP/3. Pour plus d’informations, consultez Utiliser le protocole HTTP/3 avec le serveur web ASP.NET Core Kestrel et l’entrée de blog Prise en charge du protocole HTTP/3 dans .NET 6.

Nouvelles catégories de journalisation de Kestrel pour la journalisation sélectionnée

Avant cette modification, l’activation de la journalisation détaillée pour Kestrel était extrêmement coûteuse, car tous les éléments de Kestrel partageaient le nom de la catégorie de journalisation Microsoft.AspNetCore.Server.Kestrel. La catégorie Microsoft.AspNetCore.Server.Kestrel est toujours disponible, mais les nouvelles sous-catégories suivantes permettent de mieux contrôler la journalisation :

  • Microsoft.AspNetCore.Server.Kestrel (catégorie actuelle) : ApplicationError, ConnectionHeadResponseBodyWrite, ApplicationNeverCompleted, RequestBodyStart, RequestBodyDone, RequestBodyNotEntirelyRead, RequestBodyDrainTimedOut, ResponseMinimumDataRateNotSatisfied, InvalidResponseHeaderRemoved et HeartbeatSlow.
  • Microsoft.AspNetCore.Server.Kestrel.BadRequests: ConnectionBadRequest, RequestProcessingError, RequestBodyMinimumDataRateNotSatisfied.
  • Microsoft.AspNetCore.Server.Kestrel.Connections: ConnectionAccepted, ConnectionStart, ConnectionStop, ConnectionPause, ConnectionResume, ConnectionKeepAlive, ConnectionRejected, ConnectionDisconnect, NotAllConnectionsClosedGracefully, NotAllConnectionsAborted, ApplicationAbortedConnection.
  • Microsoft.AspNetCore.Server.Kestrel.Http2: Http2ConnectionError, Http2ConnectionClosing, Http2ConnectionClosed, Http2StreamError, Http2StreamResetAbort, HPackDecodingError, HPackEncodingError, Http2FrameReceived, Http2FrameSending, Http2MaxConcurrentStreamsReached.
  • Microsoft.AspNetCore.Server.Kestrel.Http3: Http3ConnectionError, Http3ConnectionClosing, Http3ConnectionClosed, Http3StreamAbort, Http3FrameReceived, Http3FrameSending.

Les règles existantes fonctionnent toujours, mais vous pouvez désormais faire preuve d’une plus grande sélectivité en matière des règles que vous activez. Par exemple, la surcharge d’observabilité liée à l’activation de la journalisation Debug pour les requêtes incorrectes est considérablement réduite et peut être activée à l’aide de la configuration suivante :

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.Kestrel.BadRequests": "Debug"
    }
  }

Le filtrage des journaux applique des règles comportant le préfixe de catégorie à correspondance la plus longue. Pour plus d’informations, consultez Application des règles de filtrage

Émission de KestrelServerOptions via l’événement EventSource

Le KestrelEventSource émet un nouvel événement contenant le JSON sérialisé KestrelServerOptions lorsqu’il est activé avec la verbosité EventLevel.LogAlways. Cet événement facilite le raisonnement du comportement du serveur lors de l’analyse des traces collectées. Le JSON suivant représente un exemple de la charge utile de l’événement :

{
  "AllowSynchronousIO": false,
  "AddServerHeader": true,
  "AllowAlternateSchemes": false,
  "AllowResponseHeaderCompression": true,
  "EnableAltSvc": false,
  "IsDevCertLoaded": true,
  "RequestHeaderEncodingSelector": "default",
  "ResponseHeaderEncodingSelector": "default",
  "Limits": {
    "KeepAliveTimeout": "00:02:10",
    "MaxConcurrentConnections": null,
    "MaxConcurrentUpgradedConnections": null,
    "MaxRequestBodySize": 30000000,
    "MaxRequestBufferSize": 1048576,
    "MaxRequestHeaderCount": 100,
    "MaxRequestHeadersTotalSize": 32768,
    "MaxRequestLineSize": 8192,
    "MaxResponseBufferSize": 65536,
    "MinRequestBodyDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
    "MinResponseDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
    "RequestHeadersTimeout": "00:00:30",
    "Http2": {
      "MaxStreamsPerConnection": 100,
      "HeaderTableSize": 4096,
      "MaxFrameSize": 16384,
      "MaxRequestHeaderFieldSize": 16384,
      "InitialConnectionWindowSize": 131072,
      "InitialStreamWindowSize": 98304,
      "KeepAlivePingDelay": "10675199.02:48:05.4775807",
      "KeepAlivePingTimeout": "00:00:20"
    },
    "Http3": {
      "HeaderTableSize": 0,
      "MaxRequestHeaderFieldSize": 16384
    }
  },
  "ListenOptions": [
    {
      "Address": "https://127.0.0.1:7030",
      "IsTls": true,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "https://[::1]:7030",
      "IsTls": true,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "http://127.0.0.1:5030",
      "IsTls": false,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "http://[::1]:5030",
      "IsTls": false,
      "Protocols": "Http1AndHttp2"
    }
  ]
}

Nouvel événement DiagnosticSource pour les requêtes HTTP rejetées

Kestrel émet désormais un nouvel événement DiagnosticSource pour les requêtes HTTP rejetées au niveau de la couche de serveur. Il n’existait aucun moyen d’observer ces requêtes rejetées avant ce changement. Le nouvel événement DiagnosticSourceMicrosoft.AspNetCore.Server.Kestrel.BadRequest contient un IBadRequestExceptionFeature qui peut être utilisé pour l’introspection de la raison pour laquelle la requête a été rejetée.

using Microsoft.AspNetCore.Http.Features;
using System.Diagnostics;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var diagnosticSource = app.Services.GetRequiredService<DiagnosticListener>();
using var badRequestListener = new BadRequestEventListener(diagnosticSource,
    (badRequestExceptionFeature) =>
{
    app.Logger.LogError(badRequestExceptionFeature.Error, "Bad request received");
});
app.MapGet("/", () => "Hello world");

app.Run();

class BadRequestEventListener : IObserver<KeyValuePair<string, object>>, IDisposable
{
    private readonly IDisposable _subscription;
    private readonly Action<IBadRequestExceptionFeature> _callback;

    public BadRequestEventListener(DiagnosticListener diagnosticListener,
                                   Action<IBadRequestExceptionFeature> callback)
    {
        _subscription = diagnosticListener.Subscribe(this!, IsEnabled);
        _callback = callback;
    }
    private static readonly Predicate<string> IsEnabled = (provider) => provider switch
    {
        "Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true,
        _ => false
    };
    public void OnNext(KeyValuePair<string, object> pair)
    {
        if (pair.Value is IFeatureCollection featureCollection)
        {
            var badRequestFeature = featureCollection.Get<IBadRequestExceptionFeature>();

            if (badRequestFeature is not null)
            {
                _callback(badRequestFeature);
            }
        }
    }
    public void OnError(Exception error) { }
    public void OnCompleted() { }
    public virtual void Dispose() => _subscription.Dispose();
}

Pour plus d’informations, consultez Journalisation et diagnostics dans Kestrel.

Création d’un ConnectionContext à partir d’un socket Accept

Le nouveau SocketConnectionContextFactory permet de créer un ConnectionContext à partir d’un socket accepté. Cela permet de générer un IConnectionListenerFactory personnalisé basé sur un socket sans perdre tout le travail de performances et le regroupement qui se produisent dans SocketConnection.

Consultez cet exemple d’un IConnectionListenerFactory personnalisé qui présente la manière d’utiliser ce SocketConnectionContextFactory.

Kestrel est le profil de lancement par défaut pour Visual Studio

Kestrel est le profil de lancement par défaut pour tous les nouveaux projets web dotnet. Le démarrage de Kestrel est beaucoup plus rapide et favorise une expérience plus réactive lors du développement d’applications.

IIS Express est toujours disponible en tant que profil de lancement pour des scénarios tels que l’authentification Windows ou le partage de ports.

Les ports localhost pour Kestrel sont aléatoires

Pour plus d’informations, consultez Ports générés par un modèle pour Kestrel dans le présent document.

Authentification et autorisation

Serveurs d’authentification

.NET 3 à .NET 5 utilisaient IdentityServer4 dans le cadre de notre modèle pour prendre en charge l’émission de jetons JWT pour les applications monopage et Blazor. Les modèles utilisent désormais le DuendeIdentity Server.

Si vous êtes en train d’étendre les modèles d’identité et de mettre à jour des projets existants, vous devez mettre à jour les espaces de noms dans votre code de IdentityServer4.IdentityServer à Duende.IdentityServer et suivre leurs instructions de migration.

Le modèle de licence pour Duende Identity Server est passé à une licence réciproque, dont l’utilisation commerciale en production peut entraîner des frais. Pour plus d’informations, consultez la page de licence Duende.

Négociation différée du certificat client

Les développeurs peuvent désormais choisir d’utiliser la négociation différée du certificat client en spécifiant ClientCertificateMode.DelayCertificate sur le HttpsConnectionAdapterOptions. Cela fonctionne uniquement avec les connexions HTTP/1.1, car le protocole HTTP/2 interdit la renégociation différée des certificats. L’appelant de cette API doit mettre le corps de la demande en mémoire tampon avant de demander le certificat client :

using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.WebUtilities;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(options =>
{
    options.ConfigureHttpsDefaults(adapterOptions =>
    {
        adapterOptions.ClientCertificateMode = ClientCertificateMode.DelayCertificate;
    });
});

var app = builder.Build();
app.Use(async (context, next) =>
{
    bool desiredState = GetDesiredState();
    // Check if your desired criteria is met
    if (desiredState)
    {
        // Buffer the request body
        context.Request.EnableBuffering();
        var body = context.Request.Body;
        await body.DrainAsync(context.RequestAborted);
        body.Position = 0;

        // Request client certificate
        var cert = await context.Connection.GetClientCertificateAsync();

        //  Disable buffering on future requests if the client doesn't provide a cert
    }
    await next(context);
});


app.MapGet("/", () => "Hello World!");
app.Run();

L’expiration décalée de l’authentification Cookie peut désormais être personnalisée ou supprimée à l’aide du nouveau OnCheckSlidingExpiration. Par exemple, cet événement peut être utilisé par une application monopage qui a besoin d’effectuer régulièrement un test ping sur le serveur sans affecter la session d’authentification.

Divers

Rechargement à chaud

Réalisez rapidement des mises à jour d’interface utilisateur et de code pour les applications en cours d’exécution sans perdre l’état de l’application pour une expérience de développement plus rapide et plus productive à l’aide du Rechargement à chaud. Pour plus d’informations, consultez Prise en charge du .NET rechargement à chaud pour ASP.NET Core et Mise à jour sur les progrès de .NET rechargement à chaud et points clés de Visual Studio 2022.

Modèles d’applications monopages (SPA) améliorées

Les modèles de projet ASP.NET Core ont été mis à jour pour Angular et React de manière à utiliser un modèle amélioré pour les applications monopages, présentant plus de flexibilité et un alignement plus étroit sur les modèles courants du développement web front-end moderne.

Auparavant, le modèle ASP.NET Core pour Angular et React utilisait un intergiciel spécialisé pendant le développement afin de lancer le serveur de développement de l’infrastructure front-end, puis de proxyser les requêtes de ASP.NET Core vers le serveur de développement. La logique de lancement du serveur de développement front-end était spécifique à l’interface de ligne de commande de l’infrastructure front-end correspondante. La prise en charge d’infrastructures front-end supplémentaires à l’aide de ce modèle impliquait l’ajout d’une logique supplémentaire à ASP.NET Core.

Les modèles ASP.NET Core mis à jour pour Angular et React dans .NET 6 inversent cette disposition et tirent parti de la prise en charge de proxy intégrée dans les serveurs de développement de la plupart des infrastructures front-end modernes. Quand l’application ASP.NET Core est lancée, le serveur de développement front-end est lancé comme avant, mais le serveur de développement est configuré pour proxyser des requêtes au processus back-end d’ASP.NET Core. Toute la configuration spécifique du front-end pour configurer la proxysation fait partie de l’application, et non d’ASP.NET Core. La configuration de projets ASP.NET Core pour fonctionner avec d’autres infrastructures front-end est désormais simple : configurez le serveur de développement front-end pour l’infrastructure choisie de manière à proxyser vers le back-end d’ASP.NET Core à l’aide du modèle établi dans les modèles Angular et React.

Le code de démarrage de l’application ASP.NET Core ne nécessite plus une logique spécifique à l’application monopage. La logique de démarrage du serveur de développement front-end au cours du développement consiste à injecter le nouveau package Microsoft.AspNetCore.SpaProxy dans l’application au moment de l’exécution. Le routage de secours est géré à l’aide d’un routage de point de terminaison au lieu d’un intergiciel spécifique à l’application monopage.

Les modèles qui suivent ce modèle peuvent toujours être exécutés dans Visual Studio en tant que projet unique ou utiliser dotnet run de la ligne de commande. Lors de la publication de l’application, le code front-end du dossier ClientApp est généré et collecté comme avant dans la racine web de l’application hôte ASP.NET Core et délivré sous forme de fichiers statiques. Les scripts inclus dans le modèle configurent le serveur de développement front-end à utiliser le protocole HTTPS à l’aide du certificat de développement ASP.NET Core.

Brouillon de la prise en charge du protocole HTTP/3 dans .NET 6

HTTP/3 est actuellement en état de brouillon et peut donc faire l’objet de modifications. La prise en charge du protocole HTTP/3 dans ASP.NET Core n’est pas publiée, il s’agit d’une fonctionnalité d’évaluation incluse dans .NET 6.

Consultez l’entrée de blog Prise en charge du protocole HTTP/3 dans .NET 6.

Annotations de type de référence Nullable

Certaines parties du code source ASP.NET Core 6.0 ont fait l’objet d’une application d’annotations de possibilité de valeur null.

L’utilisation de la nouvelle fonctionnalité Nullable dans C# 8 permet à ASP.NET Core de fournir une sécurité supplémentaire au moment de la compilation lors de la gestion des types référence. Par exemple, la protection contre les exceptions de référence null. Les projets qui ont choisi d’utiliser des annotations pouvant accepter la valeur Null peuvent voir de nouveaux avertissements au moment de la génération de la part des API d’ASP.NET Core.

Pour activer les types référence pouvant accepter la valeur Null, ajoutez la propriété suivante aux fichiers du projet :

<PropertyGroup>
    <Nullable>enable</Nullable>
</PropertyGroup>

Pour plus d’informations, consultez Types référence pouvant accepter la valeur Null.

Analyse du code source

Plusieurs analyseurs de plateforme du compilateur .NET ont été ajoutés pour inspecter le code de l’application à la recherche de problèmes tels que la configuration ou l’ordre incorrects de l’intergiciel, les conflits de routage, etc. Pour plus d’informations, consultez Analyse du code dans les applications ASP.NET Core.

Améliorations apportées aux modèles d’application web

Les modèles d’application web :

  • Utilisent le nouveau modèle d’hébergement minimal.
  • Réduit considérablement le nombre de fichiers et de lignes de code nécessaires pour créer une application. Par exemple, l’application web ASP.NET Core vide crée un fichier C# avec quatre lignes de code qui est une application complète.
  • Unifie Startup.cs et Program.cs dans un seul fichier Program.cs.
  • Utilise des instructions de niveau supérieur pour réduire le code nécessaire pour une application.
  • Utilise des directives using globales pour éliminer ou réduire le nombre de lignes d’instructionusing nécessaires.

Ports générés par le modèle pour Kestrel

Des ports aléatoires sont attribués lors de la création du projet pour être utilisés par le serveur web Kestrel. Les ports aléatoires permettent de réduire les conflits entre les ports lorsque plusieurs projets sont exécutés sur le même ordinateur.

Lors de la création d’un projet, un port HTTP aléatoire compris entre 5000 et 5300 et un port HTTPS aléatoire compris entre 7000 et 7300 sont spécifiés dans le fichier Properties/launchSettings.json généré. Les ports peuvent être modifiés dans le fichier Properties/launchSettings.json. Si aucun port n’est spécifié, Kestrel utilise les ports par défaut HTTP 5000 et HTTPS 5001. Pour plus d’informations, consultez Configurer des points de terminaison pour le serveur web ASP.NET Core Kestrel.

Nouvelles valeurs par défaut pour la journalisation

Les modifications suivantes ont été apportées à la fois à appsettings.json et à appsettings.Development.json :

- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Microsoft.AspNetCore": "Warning"

Le passage de "Microsoft": "Warning" à "Microsoft.AspNetCore": "Warning" entraîne la journalisation de tous les messages d’information de l’espace de noms Microsoft, sauf Microsoft.AspNetCore. Par exemple, Microsoft.EntityFrameworkCore est désormais journalisé au niveau des informations.

Intergiciel de la page d’exception du développeur ajouté automatiquement

Dans l’environnement de développement, DeveloperExceptionPageMiddleware est ajouté par défaut. Il n’est plus nécessaire d’ajouter le code suivant aux applications web d’interface utilisateur :

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

Prise en charge des en-têtes de demande encodés Latin1 dans HttpSysServer

HttpSysServer prend désormais en charge le décodage des en-têtes de demande encodés Latin1 en définissant la propriété UseLatin1RequestHeaders de HttpSysOptions sur true :

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys(o => o.UseLatin1RequestHeaders = true);

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Les journaux du module ASP.NET Core comprennent des timestamps et des PID

Les journaux de diagnostic améliorés du module ASP.NET Core (ANCM) pour IIS (ANCM) comprennent des timestamps et un PID du processus émettant les journaux. La journalisation des timestamps et du PID facilite le diagnostic des problèmes liés aux redémarrages des processus qui se chevauchent dans IIS lorsque plusieurs processus Worker IIS sont en cours d’exécution.

Les journaux qui en résultent ressemblent désormais à l’exemple de sortie présenté ci-dessous :

[2021-07-28T19:23:44.076Z, PID: 11020] [aspnetcorev2.dll] Initializing logs for 'C:\<path>\aspnetcorev2.dll'. Process Id: 11020. File Version: 16.0.21209.0. Description: IIS ASP.NET Core Module V2. Commit: 96475a2acdf50d7599ba8e96583fa73efbe27912.
[2021-07-28T19:23:44.079Z, PID: 11020] [aspnetcorev2.dll] Resolving hostfxr parameters for application: '.\InProcessWebSite.exe' arguments: '' path: 'C:\Temp\e86ac4e9ced24bb6bacf1a9415e70753\'
[2021-07-28T19:23:44.080Z, PID: 11020] [aspnetcorev2.dll] Known dotnet.exe location: ''

Taille de la mémoire tampon entrante non consommée configurable pour IIS

Auparavant, le serveur IIS ne mettait dans la mémoire tampon que 64 Kio des corps de requêtes non consommés. La mise en mémoire tampon de 64 Kio a entraîné la contrainte des lectures à cette taille maximale, ce qui affecte le niveau de performance avec les corps entrants volumineux tels que les chargements. Dans .NET 6, la taille de la mémoire tampon par défaut passe de 64 Kio à 1 Mio, ce qui devrait améliorer le débit des chargements volumineux. Dans nos tests, un chargement de 700 Mio qui prenait auparavant 9 secondes ne prend plus que 2,5 secondes.

L’inconvénient d’une taille de mémoire tampon plus importante réside dans l’augmentation de la consommation de mémoire par requête lorsque l’application ne lit pas rapidement à partir du corps de la demande. Ainsi, en plus d’avoir apporté des modifications à la taille de la mémoire tampon par défaut, celle-ci est configurable, ce qui permet aux applications de configurer la taille de la mémoire tampon en fonction de la charge de travail.

Afficher les Tag Helpers des composants

Envisagez un composant d’affichage doté d’un paramètre facultatif, comme indiqué dans le code suivant :

class MyViewComponent
{
    IViewComponentResult Invoke(bool showSomething = false) { ... }
}

ASP.NET Core 6 permet d’appeler le l’assistant de balise sans devoir spécifier une valeur pour le paramètre showSomething :

<vc:my />

Mise à jour du modèle Angular vers Angular 12

Le modèle ASP.NET Core 6.0 pour Angular utilise désormais Angular 12.

Le modèle React a été mis à jour vers React 17.

Seuil de mémoire tampon configurable avant l’écriture sur disque dans le formateur de sortie Json.NET

Remarque : Nous recommandons l’utilisation du formateur de sortie System.Text.Json sauf dans les cas où un sérialiseur Newtonsoft.Json est nécessaire pour des raisons de compatibilité. Le sérialiseur System.Text.Json est entièrement async et fonctionne efficacement pour les charges utiles plus volumineuses.

Le formateur de sortie Newtonsoft.Json par défaut met les réponses allant jusqu’à 32 Kio en mémoire tampon avant la mise en mémoire tampon sur le disque. Cela permet d’éviter d’effectuer les E/S synchrones, ce qui peut entraîner d’autres effets secondaires tels qu’une insuffisance de threads et des interblocages d’applications. Toutefois, si la réponse est plus volumineuse que 32 Kio, des E/S de disque considérables se produisent. Le seuil de mémoire est désormais configurable via la propriété MvcNewtonsoftJsonOptions.OutputFormatterMemoryBufferThreshold avant la mise en mémoire tampon sur le disque :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages()
            .AddNewtonsoftJson(options =>
            { 
                options.OutputFormatterMemoryBufferThreshold = 48 * 1024;
            });

var app = builder.Build();

Pour plus d’informations, consultez cette demande de tirage (pull request) GitHub et le fichier NewtonsoftJsonOutputFormatterTest.cs.

Get et set plus rapides pour les en-têtes HTTP

De nouvelles API ont été ajoutées pour exposer tous les en-têtes courants disponibles sur Microsoft.Net.Http.Headers.HeaderNames en tant que propriétés sur le IHeaderDictionary, ce qui a rendu l’API plus facile à utiliser. Par exemple, l’intergiciel en ligne du code suivant obtient et définit à la fois les en-têtes de requête et de réponse à l’aide des nouvelles API :

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Use(async (context, next) =>
{
    var hostHeader = context.Request.Headers.Host;
    app.Logger.LogInformation("Host header: {host}", hostHeader);
    context.Response.Headers.XPoweredBy = "ASP.NET Core 6.0";
    await next.Invoke(context);
    var dateHeader = context.Response.Headers.Date;
    app.Logger.LogInformation("Response date: {date}", dateHeader);
});

app.Run();

Pour les en-têtes implémentés, les accesseurs get et set sont implémentés par un accès direct au champ et un contournement de la recherche. Pour les en-têtes non implémentés, les accesseurs peuvent contourner la recherche initiale par rapport aux en-têtes implémentés et effectuer directement la recherche de Dictionary<string, StringValues>. Éviter la recherche entraîne un accès plus rapide pour les deux scénarios.

Diffusion en continu asynchrone

ASP.NET Core prend désormais en charge la diffusion en continu asynchrone à partir des actions du contrôleur et des réponses du Formateur JSON. Le retour d’un IAsyncEnumerable à partir d’une action ne met plus en mémoire tampon le contenu de la réponse avant de l’envoyer. Le fait de ne pas mettre en mémoire tampon permet une utilisation plus réduite de la mémoire lors du retour de jeux de données volumineux qui peuvent être énumérés de manière asynchrone.

Il faut noter qu’Entity Framework Core fournit des implémentations de IAsyncEnumerable pour interroger la base de données. La prise en charge améliorée de IAsyncEnumerable dans ASP.NET Core dans .NET 6 peut améliorer l’efficacité de l’utilisation de EF Core avec ASP.NET Core. Par exemple, le code suivant ne met plus les données du produit en mémoire tampon avant d’envoyer la réponse :

public IActionResult GetMovies()
{
    return Ok(_context.Movie);
}

Toutefois, lors de l’utilisation du chargement différé dans EF Core, ce nouveau comportement peut entraîner des erreurs en raison de l’exécution simultanée des requêtes pendant l’énumération des données. Les applications peuvent revenir au comportement précédent en mettant les données en mémoire tampon :

public async Task<IActionResult> GetMovies2()
{
    return Ok(await _context.Movie.ToListAsync());
}

Pour plus d’informations sur ce changement de comportement, consultez l’annonce connexe.

Intergiciel de la journalisation HTTP

La journalisation HTTP est un nouvel intergiciel intégré qui consigne les informations des requêtes HTTP et des réponses HTTP, y compris les en-têtes et l’ensemble du corps :

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();
app.UseHttpLogging();

app.MapGet("/", () => "Hello World!");

app.Run();

L’accès à / avec le code précédent consigne des informations similaires à la sortie suivante :

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/2
      Method: GET
      Scheme: https
      PathBase: 
      Path: /
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Cache-Control: max-age=0
      Connection: close
      Cookie: [Redacted]
      Host: localhost:44372
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30
      sec-ch-ua: [Redacted]
      sec-ch-ua-mobile: [Redacted]
      sec-ch-ua-platform: [Redacted]
      upgrade-insecure-requests: [Redacted]
      sec-fetch-site: [Redacted]
      sec-fetch-mode: [Redacted]
      sec-fetch-user: [Redacted]
      sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/plain; charset=utf-8

La sortie précédente a été activée à l’aide du fichier appsettings.Development.json suivant :

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
    }
  }
}

La journalisation HTTP fournit les journaux suivants :

  • Informations de requête HTTP
  • Propriétés communes
  • En-têtes
  • Corps
  • Informations de réponse HTTP

Pour configurer l’intergiciel de journalisation HTTP, spécifiez HttpLoggingOptions :

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(logging =>
{
    // Customize HTTP logging.
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("My-Request-Header");
    logging.ResponseHeaders.Add("My-Response-Header");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
});

var app = builder.Build();
app.UseHttpLogging();

app.MapGet("/", () => "Hello World!");

app.Run();

IConnectionSocketFeature

La fonctionnalité de requête IConnectionSocketFeature fournit un accès au socket Accept sous-jacent associé à la requête actuelle. Celle-ci est accessible via le FeatureCollection sur HttpContext.

Par exemple, l’application suivante définit la propriété LingerState sur le socket accepté :

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
    {
        var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
        socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
        return next();
    }));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();

Contraintes de type générique dans Razor

Lors de la définition de paramètres de type générique dans Razor à l’aide de la directive @typeparam, les contraintes de type générique peuvent désormais être spécifiées à l’aide de la syntaxe C# standard :

Scripts SignalR, Blazor Server et MessagePack plus petits

Les scripts SignalR, MessagePack et Blazor Server sont désormais beaucoup plus petits, ce qui favorise des téléchargements plus petits, moins d’analyse et de compilation JavaScript par le navigateur et un démarrage plus rapide. Les réductions de taille :

  • signalr.js : 70 %
  • blazor.server.js : 45 %

Les scripts plus petits ont résulté de la contribution de la communauté de Ben Adams. Pour plus d’informations et des détails sur la réduction de taille, consultez Demande de tirage (pull request) GitHub de Ben.

Activer les sessions de profilage Redis

Une contribution de la communauté de Gabriel Lucaci active la session de profilage Redis avec Microsoft.Extensions.Caching.StackExchangeRedis :

using StackExchange.Redis.Profiling;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.ProfilingSession = () => new ProfilingSession();
});

Pour plus d’informations, consultez Profilage de StackExchange.Redis.

Cliché instantané dans IIS

Une fonctionnalité expérimentale a été ajoutée au Module ASP.NET Core (ANCM) pour IIS pour ajouter la prise en charge des clichés instantanés des assemblys d’applications. À l’heure actuelle, .NET verrouille les fichiers binaires d’application lors de l’exécution sur Windows, ce qui rend le remplacement des fichiers binaires impossible quand l’application est en cours d’exécution. Bien que notre recommandation reste toujours d’utiliser un fichier d’application hors connexion, nous reconnaissons qu’il existe certains scénarios (par exemple des déploiements FTP) dans lesquels cela n’est pas possible.

Dans ces scénarios, vous devez activer le cliché instantané en personnalisant les paramètres du gestionnaire du module ASP.NET Core. Dans la plupart des cas, les applications ASP.NET Core ne disposent pas d’un web.config archivé dans le contrôle de code source que vous pouvez modifier. Dans ASP.NET Core, web.config est généralement généré par le kit de développement logiciel (SDK). Vous pouvez commencer par l’exemple de web.config suivant :

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- To customize the asp.net core module uncomment and edit the following section. 
  For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->

  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
      <handlerSettings>
        <handlerSetting name="experimentalEnableShadowCopy" value="true" />
        <handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
        <!-- Only enable handler logging if you encounter issues-->
        <!--<handlerSetting name="debugFile" value=".\logs\aspnetcore-debug.log" />-->
        <!--<handlerSetting name="debugLevel" value="FILE,TRACE" />-->
      </handlerSettings>
    </aspNetCore>
  </system.webServer>
</configuration>

Le cliché instantané dans IIS est une fonctionnalité expérimentale dont l’intégration à ASP.NET Core n’est pas garantie. Veuillez laisser des commentaires sur le cliché instantané IIS dans ce problème GitHub.

Ressources supplémentaires