Partager via


Nouveautés d’ASP.NET Core 7.0

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

Intergiciel de réécriture d’URL dans ASP.NET Core

L’intergiciel Microsoft.AspNetCore.RateLimiting fournit un intergiciel de limitation de débit. Les applications configurent des stratégies de limitation du débit, puis attachent les stratégies aux points de terminaison. Pour plus d’informations, consultez Intergiciel de limitation du débit dans ASP.NET Core.

L’authentification utilise un schéma unique comme DefaultScheme

Dans le cadre du travail de simplification de l’authentification, lorsqu’un seul schéma d’authentification est inscrit, il est automatiquement utilisé comme DefaultScheme et n’a pas besoin d’être spécifié. Pour plus d’informations, consultez DefaultScheme.

MVC et Razor Pages

Prise en charge des modèles nullables dans les vues MVC et Razor Pages

Les modèles de page ou de vue nullables sont pris en charge pour améliorer l’expérience lors de l’utilisation de la vérification de l’état Null avec les applications ASP.NET Core :

@model Product?

Lier avec IParsable<T>.TryParse dans MVC et les contrôleurs d’API

L’API IParsable<TSelf>.TryParse prend en charge les valeurs de paramètre d’action du contrôleur de liaison. Pour plus d’informations, consultez Lier avec IParsable<T>.TryParse.

Dans les versions antérieures d’ASP.NET Core antérieures à 7, la validation du consentement cookie utilise la valeur cookieyes pour indiquer le consentement. Vous pouvez maintenant spécifier la valeur qui représente le consentement. Par exemple, vous pouvez utiliser true au lieu de yes :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
    options.ConsentCookieValue = "true";
});

var app = builder.Build();

Pour plus d’informations, consultez Personnaliser la valeur de consentement cookie.

Contrôleurs d’API

Liaison de paramètres avec la DI dans les contrôleurs d’API

La liaison de paramètre pour les actions de contrôleur d’API lie des paramètres via l’injection de dépendance quand le type est configuré en tant que service. Cela signifie qu’il n’est plus nécessaire d’appliquer explicitement l’attribut [FromServices] à un paramètre. Dans le code suivant, les deux actions retournent l’heure :

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

Dans de rares cas, l’injection de dépendances automatique peut interrompre les applications qui présentent un type dans l’injection de dépendances qui est également accepté dans les méthodes d’action d’un contrôleur d’API. Il n’est pas courant d’avoir un type dans l’injection de dépendances et comme argument dans une action du contrôleur d’API. Pour désactiver la liaison automatique de paramètres, définissez DisableImplicitFromServicesParameters

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

Dans ASP.NET Core 7.0, les types dans l’injection de dépendance sont vérifiés au démarrage de l’application avec IServiceProviderIsService pour déterminer si un argument dans une action de contrôleur d’API provient de l’injection de dépendances ou d’autres sources.

Le nouveau mécanisme permettant de déduire la source de liaison des paramètres d’action du contrôleur d’API utilise les règles suivantes :

  1. Un BindingInfo.BindingSource spécifié précédemment n’est jamais remplacé.
  2. BindingSource.Services est affecté à un paramètre de type complexe, inscrit dans le conteneur d’injection de dépendances.
  3. BindingSource.Body est affecté à un paramètre de type complexe, qui n’est pas inscrit dans le conteneur d’injection de dépendances.
  4. BindingSource.Path est affecté à un paramètre avec un nom qui apparaît en tant que valeur de routage dans n’importe quel modèle de routage.
  5. Tous les autres paramètres sont BindingSource.Query.

Noms de propriétés JSON dans les erreurs de validation

Par défaut, lorsqu’une erreur de validation se produit, la validation du modèle génère un ModelStateDictionary avec le nom de propriété comme clé d’erreur. Certaines applications, comme les applications monopages, tirent parti de l’utilisation de noms de propriétés JSON pour les erreurs de validation générées à partir des API web. Le code suivant configure la validation pour qu’elle utilise les noms de propriété JSON dans le format SystemTextJsonValidationMetadataProvider :

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Le code suivant configure la validation pour utiliser NewtonsoftJsonValidationMetadataProvider afin d’utiliser les noms de propriété JSON lors de l’utilisation de Json.NET :

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Pour plus d’informations, consultez Utiliser les noms de propriété JSON dans les erreurs de validation.

API minimales

Filtres dans les applications d’API minimales

Les filtres d’API minimaux permettent aux développeurs d’implémenter une logique métier qui prend en charge :

  • Exécution du code avant et après le gestionnaire de routes.
  • Inspection et modification des paramètres fournis lors d’un appel de gestionnaire de routes.
  • Interception du comportement de réponse d’un gestionnaire de routes.

Les filtres peuvent être utiles dans les scénarios suivants :

  • Validation des paramètres et du corps de la requête envoyés à un point de terminaison.
  • Journalisation des informations sur la requête et la réponse.
  • Validation qu’une requête cible une version d’API prise en charge.

Pour plus d’informations, consultez Filtres dans les applications API minimales

Lier des tableaux et des valeurs de chaîne à partir d’en-têtes et de chaînes de requête

Dans ASP.NET 7, la liaison de chaînes de requête à un tableau de types primitifs, de tableaux de chaînes et de StringValues est prise en charge :

// Bind query string values to a primitive type array.
// GET  /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) =>
                      $"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}");

// Bind to a string array.
// GET /tags2?names=john&names=jack&names=jane
app.MapGet("/tags2", (string[] names) =>
            $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");

// Bind to StringValues.
// GET /tags3?names=john&names=jack&names=jane
app.MapGet("/tags3", (StringValues names) =>
            $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");

La liaison de chaînes de requête ou de valeurs d’en-tête à un tableau de types complexes est prise en charge lorsque le type implémente TryParse. Pour plus d’informations, consultez Lier des tableaux et des valeurs de chaîne à partir d’en-têtes et de chaînes de requête.

Pour plus d’informations, consultez Ajouter un résumé ou une description de point de terminaison.

Lier le corps de la requête en tant que Stream ou PipeReader

Le corps de la requête peut être lié en tant que Stream ou PipeReader pour prendre en charge efficacement les scénarios où l’utilisateur doit traiter des données et :

  • Stockez les données dans le stockage d’objets blob ou placez les données en file d’attente dans un fournisseur de file d’attente.
  • Traitez les données stockées avec un processus Worker ou une fonction cloud.

Par exemple, les données peuvent être mises en file d’attente pour le Stockage File d’attente Azure ou stockées dans le Stockage Blob Azure.

Pour plus d’informations, consultez Lier le corps de la requête en tant que Stream ou PipeReader

Nouvelles surcharges Results.Stream

Nous avons introduit de nouvelles surcharges de Results.Stream pour prendre en charge les scénarios qui nécessitent d’accéder au flux de réponse HTTP sous-jacent sans mettre en mémoire tampon. Ces surcharges améliorent également les cas où une API diffuse des données vers le flux de réponse HTTP, comme à partir du Stockage Blob Azure. L’exemple suivant utilise ImageSharp pour retourner une taille réduite de l’image spécifiée :

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/process-image/{strImage}", (string strImage, HttpContext http, CancellationToken token) =>
{
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";
    return Results.Stream(stream => ResizeImageAsync(strImage, stream, token), "image/jpeg");
});

async Task ResizeImageAsync(string strImage, Stream stream, CancellationToken token)
{
    var strPath = $"wwwroot/img/{strImage}";
    using var image = await Image.LoadAsync(strPath, token);
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x =>x.Resize(width, height));
    await image.SaveAsync(stream, JpegFormat.Instance, cancellationToken: token);
}

Pour plus d’informations, consultez Exemples de diffusion en continu

Résultats typés pour les API minimales

Dans .NET 6, l’interface IResult a été introduite pour représenter les valeurs retournées par les API minimales qui n’utilisent pas la prise en charge implicite de la sérialisation JSON de l’objet retourné dans la réponse HTTP. La classe statique Results est utilisée pour créer différents objets IResult qui représentent différents types de réponses. Par exemple, la définition du code d’état de la réponse ou la redirection vers une autre URL. Toutefois, les types de framework implémentant IResult retournés par ces méthodes étaient internes, ce qui rendait difficile la vérification du type IResult spécifique retourné par les méthodes dans un test unitaire.

Dans .NET 7, les types implémentant IResult sont publics, ce qui permet les assertions de type lors des tests. Par exemple :

[TestClass()]
public class WeatherApiTests
{
    [TestMethod()]
    public void MapWeatherApiTest()
    {
        var result = WeatherApi.GetAllWeathers();
        Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
    }      
}

Testabilité unitaire améliorée pour les gestionnaires de routes minimaux

Les types d’implémentation IResult sont désormais disponibles publiquement dans l’espace de noms Microsoft.AspNetCore.Http.HttpResults. Les types d’implémentation IResult peuvent être utilisés pour tester unitairement des gestionnaires de routes minimaux lors de l’utilisation de méthodes nommées au lieu de lambdas.

Le code suivant utilise la classe Ok<TValue> :

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title",
        Description = "Test description",
        IsDone = false
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var okResult = (Ok<Todo>)result.Result;

    Assert.NotNull(okResult.Value);
    Assert.Equal(1, okResult.Value.Id);
}

Pour plus d’informations, consultez Types d’implémentation IResult.

Nouvelles interfaces HttpResult

Les interfaces suivantes dans l’espace de noms Microsoft.AspNetCore.Http permettent de détecter le type IResult au moment de l’exécution, ce qui est un modèle courant dans les implémentations de filtre :

Pour plus d’informations, consultez Interfaces IHttpResult.

Améliorations d’OpenAPI pour les API minimales

package NuGet Microsoft.AspNetCore.OpenApi

Le package Microsoft.AspNetCore.OpenApi autorise les interactions avec les spécifications OpenAPI pour les points de terminaison. Le package agit comme un lien entre les modèles OpenAPI définis dans le package Microsoft.AspNetCore.OpenApi et les points de terminaison définis dans les API minimales. Le package fournit une API qui examine les paramètres, les réponses et les métadonnées d’un point de terminaison pour construire un type d’annotation OpenAPI utilisé pour décrire un point de terminaison.

app.MapPost("/todoitems/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi();

Appeler WithOpenApi avec des paramètres

La méthode WithOpenApi accepte une fonction qui peut être utilisée pour modifier l’annotation OpenAPI. Par exemple, dans le code suivant, une description est ajoutée au premier paramètre du point de terminaison :

app.MapPost("/todo2/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi(generatedOperation =>
{
    var parameter = generatedOperation.Parameters[0];
    parameter.Description = "The ID associated with the created Todo";
    return generatedOperation;
});

Fournir des descriptions et des résumés des points de terminaison

Les API minimales prennent désormais en charge les opérations d’annotation avec des descriptions et des résumés pour la génération de spécifications OpenAPI. Vous pouvez appeler des méthodes d’extension WithDescription et WithSummary ou utiliser des attributs [EndpointDescription] et [EndpointSummary]).

Pour plus d’informations, consultez OpenAPI dans les applications API minimales.

Chargements de fichiers à l’aide d’IFormFile et IFormFileCollection

Les API minimales prennent désormais en charge le chargement de fichiers avec IFormFile et IFormFileCollection. Le code suivant utilise IFormFile et IFormFileCollection pour charger le fichier :

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

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

app.MapPost("/upload", async (IFormFile file) =>
{
    var tempFile = Path.GetTempFileName();
    app.Logger.LogInformation(tempFile);
    using var stream = File.OpenWrite(tempFile);
    await file.CopyToAsync(stream);
});

app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        var tempFile = Path.GetTempFileName();
        app.Logger.LogInformation(tempFile);
        using var stream = File.OpenWrite(tempFile);
        await file.CopyToAsync(stream);
    }
});

app.Run();

Les requêtes de chargement de fichiers authentifiés sont prises en charge à l’aide d’un en-tête d’autorisation, d’un certificat client ou d’un en-tête cookie.

Il n’y a pas de prise en charge intégrée de l’antifalsification. Toutefois, elle peut être implémentée à l’aide du service IAntiforgery.

L’attribut [AsParameters] active la liaison de paramètres pour les listes d’arguments

L’attribut [AsParameters] active la liaison de paramètres pour les listes d’arguments. Pour plus d’informations, consultez Liaison de paramètres pour les listes d’arguments avec [AsParameters].

API minimales et contrôleurs d’API

Nouveau service de détails du problème

Le service de détails du problème implémente l’interface IProblemDetailsService, qui prend en charge la création de Détails du problème pour les API HTTP.

Pour plus d’informations, consultez Service de détails du problème.

Groupes d’itinéraires

La méthode d’extension MapGroup permet d’organiser des groupes de points de terminaison avec un préfixe commun. Cela réduit le code répétitif et permet de personnaliser des groupes entiers de points de terminaison avec un seul appel à des méthodes comme RequireAuthorization et WithMetadata, qui ajoutent des métadonnées de point de terminaison.

Par exemple, le code suivant crée deux groupes de points de terminaison similaires :

app.MapGroup("/public/todos")
    .MapTodosApi()
    .WithTags("Public");

app.MapGroup("/private/todos")
    .MapTodosApi()
    .WithTags("Private")
    .AddEndpointFilterFactory(QueryPrivateTodos)
    .RequireAuthorization();


EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
    var dbContextIndex = -1;

    foreach (var argument in factoryContext.MethodInfo.GetParameters())
    {
        if (argument.ParameterType == typeof(TodoDb))
        {
            dbContextIndex = argument.Position;
            break;
        }
    }

    // Skip filter if the method doesn't have a TodoDb parameter.
    if (dbContextIndex < 0)
    {
        return next;
    }

    return async invocationContext =>
    {
        var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
        dbContext.IsPrivate = true;

        try
        {
            return await next(invocationContext);
        }
        finally
        {
            // This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
            dbContext.IsPrivate = false;
        }
    };
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
    group.MapGet("/", GetAllTodos);
    group.MapGet("/{id}", GetTodo);
    group.MapPost("/", CreateTodo);
    group.MapPut("/{id}", UpdateTodo);
    group.MapDelete("/{id}", DeleteTodo);

    return group;
}

Dans ce scénario, vous pouvez utiliser une adresse relative pour l’en-tête Location dans le résultat 201 Created :

public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
    await database.AddAsync(todo);
    await database.SaveChangesAsync();

    return TypedResults.Created($"{todo.Id}", todo);
}

Le premier groupe de points de terminaison correspond uniquement aux requêtes précédées de /public/todos, accessibles sans authentification. Le second groupe de points de terminaison correspond uniquement aux requêtes préfixées par /private/todos, qui nécessitent une authentification.

La QueryPrivateTodosfabrique de filtre de point de terminaison est une fonction locale qui modifie les paramètres TodoDb du gestionnaire d’itinéraires pour permettre l’accès et le stockage de données todo privées.

Les groupes de routage prennent également en charge les groupes imbriqués et les modèles de préfixe complexes avec des contraintes et des paramètres de routage. Dans l’exemple suivant, un gestionnaire de routage mappé au groupe user peut capturer les paramètres de routage {org} et {group} définis dans les préfixes de groupe externe.

Le préfixe peut également être vide. Cela peut être utile pour ajouter des métadonnées ou des filtres de point de terminaison à un groupe de points de terminaison sans modifier le modèle de routage.

var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");

L’ajout de filtres ou de métadonnées à un groupe se comporte de la même façon que si vous les ajoutiez individuellement à chaque point de terminaison avant d’ajouter des filtres ou des métadonnées supplémentaires qui ont pu être ajoutés à un groupe interne ou à un point de terminaison spécifique.

var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");

inner.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/inner group filter");
    return next(context);
});

outer.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/outer group filter");
    return next(context);
});

inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("MapGet filter");
    return next(context);
});

Dans l’exemple ci-dessus, le filtre externe enregistre la requête entrante avant le filtre interne, même si elle a été ajoutée en deuxième. Étant donné que les filtres ont été appliqués à différents groupes, l’ordre dans lequel ils ont été ajoutés les uns par rapport aux autres n’a pas d’importance. Les filtres d’ordre ajoutés sont importants s’ils sont appliqués au même groupe ou au même point de terminaison spécifique.

Une requête sur /outer/inner/ journalisera les éléments suivants :

/outer group filter
/inner group filter
MapGet filter

gRPC

Transcodage JSON

Le transcodage JSON gRPC est une extension pour ASP.NET Core qui crée des API RESTful JSON pour les services gRPC. Le transcodage JSON gRPC permet :

  • Aux applications d’appeler des services gRPC avec des concepts HTTP familiers.
  • Aux applications gRPC ASP.NET Core de prendre en charge à la fois les API gRPC et JSON RESTful sans répliquer de fonctionnalités.
  • La prise en charge expérimentale de la génération d’OpenAPI à partir d’API RESTful transcodées grâce à l’intégration à Swashbuckle.

Pour plus d’informations, consultez Transcodage JSON gRPC dans les applications gRPC ASP.NET Core et Utiliser OpenAPI avec les applications transcodées JSON gRPC ASP.NET Core.

Contrôles d’intégrité gRPC dans ASP.NET Core

Le protocole de contrôle d’intégrité gRPC est une norme permettant de signaler l’intégrité des applications serveur gRPC. Une application expose des contrôles d’intégrité en tant que service gRPC. Ils sont généralement utilisés avec un service de supervision externe pour vérifier l’état d’une application.

ASP.NET Core gRPC a ajouté la prise en charge intégrée des vérifications d’intégrité gRPC avec le package Grpc.AspNetCore.HealthChecks. Les résultats des contrôles d’intégrité .NET sont signalés aux appelants.

Pour plus d’informations, consultez Contrôles d’intégrité gRPC dans ASP.NET Core.

Amélioration de la prise en charge des informations d’identification d’appel

Les informations d’identification d’appel sont la méthode recommandée pour configurer un client gRPC afin d’envoyer un jeton d’authentification au serveur. Les clients gRPC prennent en charge deux nouvelles fonctionnalités pour faciliter l’utilisation des informations d’identification d’appel :

  • Prise en charge des informations d’identification d’appel avec les connexions en texte clair. Auparavant, un appel gRPC envoyait uniquement les informations d’identification d’appel si la connexion était sécurisée avec TLS. Un nouveau paramètre sur GrpcChannelOptions, appelé UnsafeUseInsecureChannelCallCredentials, permet de personnaliser ce comportement. Il existe des implications en matière de sécurité si vous ne sécurisez pas une connexion avec TLS.
  • Une nouvelle méthode appelée AddCallCredentials est disponible avec la fabrique de clients gRPC. AddCallCredentials est un moyen rapide de configurer les informations d’identification d’appel pour un client gRPC, et s’intègre bien à l’injection de dépendances (DI).

Le code suivant configure la fabrique de clients gRPC pour envoyer des métadonnées Authorization :

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
       o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
       if (!string.IsNullOrEmpty(_token))
       {
          metadata.Add("Authorization", $"Bearer {_token}");
       }
       return Task.CompletedTask;
    });

Pour plus d’informations, consultez Configurer un jeton de porteur avec la fabrique de clients gRPC.

SignalR

Résultats du client

Le serveur prend désormais en charge la demande d’un résultat à partir d’un client. Cela nécessite que le serveur utilise ISingleClientProxy.InvokeAsync et que le client retourne un résultat à partir de son gestionnaire .On. Les hubs fortement typés peuvent également retourner des valeurs à partir de méthodes d’interface.

Pour plus d’informations, consultez Résultats du client

Injection de dépendances pour les méthodes hub SignalR

Les méthodes de hub SignalR prennent désormais en charge l’injection de services via l’injection de dépendances (DI).

Les constructeurs de hub peuvent accepter les services de DI en tant que paramètres, qui peuvent être stockés dans des propriétés sur la classe pour une utilisation dans une méthode de hub. Pour plus d’informations, consultez Injecter des services dans un hub

Blazor

Gérer les événements de changement d’emplacement et l’état de navigation

Dans .NET 7, Blazor prend en charge les événements de changement d’emplacement et le maintien de l’état de navigation. Cela vous permet d’avertir les utilisateurs concernant le travail non enregistré ou d’effectuer des actions associées lorsque l’utilisateur effectue une navigation sur une page.

Pour plus d’informations, consultez les sections suivantes de l’article Routage et navigation :

Modèles de projet Blazor vides

Blazor propose deux nouveaux modèles de projet pour commencer à partir de zéro. Les nouveaux modèles Application Blazor Server vide et Application Blazor WebAssembly vide sont semblables à leurs équivalents non vides, mais sans exemple de code. Ces modèles vides incluent uniquement une page d’home de base, et nous avons supprimé Bootstrap pour que vous puissiez commencer avec un autre framework CSS.

Pour plus d’informations, consultez les articles suivants :

Éléments personnalisés Blazor

Le package Microsoft.AspNetCore.Components.CustomElements permet de créer des éléments DOM personnalisés basés sur des normes à l’aide de Blazor.

Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Modificateurs de liaison (@bind:after, @bind:get, @bind:set)

Important

Les fonctionnalités @bind:after/@bind:get/@bind:set reçoivent actuellement d’autres mises à jour. Pour tirer parti des dernières mises à jour, vérifiez que vous avez installé la dernière version du SDK.

L’utilisation d’un paramètre de rappel d’événement ([Parameter] public EventCallback<string> ValueChanged { get; set; }) n’est pas prise en charge. Au lieu de cela, passez une retournant Action ou Task à @bind:set/@bind:after.

Pour plus d’informations, consultez les ressources suivantes :

Dans .NET 7, vous pouvez exécuter une logique asynchrone une fois qu’un événement de liaison est terminé à l’aide du nouveau modificateur @bind:after. Dans l’exemple suivant, la méthode asynchrone PerformSearch s’exécute automatiquement après la détection des modifications apportées au texte de recherche :

<input @bind="searchText" @bind:after="PerformSearch" />

@code {
    private string searchText;

    private async Task PerformSearch()
    {
        ...
    }
}

Dans .NET 7, il est également plus facile de configurer la liaison pour les paramètres de composant. Les composants peuvent prendre en charge la liaison de données bidirectionnelle en définissant une paire de paramètres :

  • @bind:get : spécifie la valeur à lier.
  • @bind:set : spécifie un rappel au moment où la valeur change.

Les modificateurs @bind:get et @bind:set sont toujours utilisés ensemble.

Exemples :

@* Elements *@

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind:get="text" @bind:set="(value) => { }" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<input type="text" @bind:get="text" @bind:set="SetAsync" />

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind:get="text" @bind:set="(value) => { }" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<input type="text" @bind:get="text" @bind:set="SetAsync" />

@* Components *@

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { }" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { }" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

@code {
    private string text = "";

    private void After(){}
    private void Set() {}
    private Task AfterAsync() { return Task.CompletedTask; }
    private Task SetAsync(string value) { return Task.CompletedTask; }
}

Pour plus d’informations sur le composant InputText, consultez Composants d’entrée Blazor ASP.NET Core.

Améliorations du rechargement à chaud

Dans .NET 7, la prise en charge du rechargement à chaud comprend les éléments suivants :

  • Les composants réinitialisent leurs paramètres à leurs valeurs par défaut lorsqu’une valeur est supprimée.
  • Blazor WebAssembly:
    • Ajout de nouveaux types.
    • Ajout des classes imbriquées.
    • Ajout de méthodes statiques et d’instance aux types existants.
    • Ajout de champs et de méthodes statiques aux types existants.
    • Ajout de lambdas statiques aux méthodes existantes.
    • Ajout de lambdas qui capturent this pour les méthodes existantes qui capturaient déjà this précédemment.

Demandes d’authentification dynamique avec MSAL dans Blazor WebAssembly

Nouveauté de .NET 7, Blazor WebAssembly prend en charge la création de requêtes d’authentification dynamique au moment de l’exécution avec des paramètres personnalisés pour gérer les scénarios d’authentification avancés.

Pour plus d’informations, consultez les articles suivants :

Améliorations du débogage de Blazor WebAssembly

Le débogage de Blazor WebAssembly présente les améliorations suivantes :

  • Prise en charge du paramètre Uniquement mon code pour afficher ou masquer les membres de type qui ne proviennent pas du code utilisateur.
  • Prise en charge de l’inspection des tableaux multidimensionnels.
  • La pile d’appels affiche désormais le nom correct pour les méthodes asynchrones.
  • Amélioration de l’évaluation des expressions.
  • Gestion correcte du mot clé new sur les membres dérivés.
  • Prise en charge des attributs liés au débogueur dans System.Diagnostics.

Prise en charge de System.Security.Cryptography sur WebAssembly

.NET 6 prenait en charge la famille SHA d’algorithmes de hachage lors de l’exécution sur WebAssembly. .NET 7 propose davantage d’algorithmes de chiffrement en tirant parti de SubtleCrypto, lorsque cela est possible, et en revenant à une implémentation .NET quand SubtleCrypto ne peut pas être utilisé. Les algorithmes suivants sont pris en charge sur WebAssembly dans .NET 7 :

  • SHA1
  • SHA256
  • SHA384
  • SHA512
  • HMACSHA1
  • HMACSHA256
  • HMACSHA384
  • HMACSHA512
  • AES-CBC
  • PBKDF2
  • HKDF

Pour plus d’informations, consultez Les développeurs ciblant browser-wasm peuvent utiliser les API de chiffrement web (dotnet/runtime #40074).

Injection de services dans des attributs de validation personnalisés

Vous pouvez maintenant injecter des services dans des attributs de validation personnalisés. Blazor configure le ValidationContext afin qu’il puisse être utilisé en tant que fournisseur de services.

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

Composants Input* en dehors d’un EditContext/EditForm

Les composants d’entrée intégrés sont désormais pris en charge en dehors d’un formulaire dans le balisage de composant Razor.

Pour obtenir plus d’informations, consultez Composants d’entrée Blazor ASP.NET Core.

Changements au modèle de projet

Lorsque .NET 6 a été publié l’année dernière, le balisage HTML de la page _Host (Pages/_Host.chstml) a été fractionné entre la page _Host et une nouvelle page _Layout (Pages/_Layout.chstml) dans le modèle de projet Blazor Server .NET 6.

Dans .NET 7, le balisage HTML a été combiné avec la page _Host dans les modèles de projet.

Plusieurs modifications supplémentaires ont été apportées aux modèles de projet Blazor. Il n’est pas possible de répertorier chaque modification apportée aux modèles dans la documentation. Pour migrer une application vers .NET 7 afin d’adopter toutes les modifications, consultez Migrer d’ASP.NET Core 6.0 vers 7.0.

Composant QuickGrid expérimental

Le nouveau composant QuickGrid fournit un composant de grille de données pratique pour les exigences les plus courantes et en tant qu’architecture de référence et base de référence pour toute personne qui crée des composants de grille de données Blazor.

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

Démonstration en direct : QuickGrid pour l’exemple d’application Blazor

Améliorations apportées à la virtualisation

Améliorations apportées à la virtualisation dans .NET 7 :

  • Le composant Virtualize prend en charge l’utilisation du document lui-même comme racine de défilement, comme alternative à l’utilisation d’un autre élément avec overflow-y: scroll appliqué.
  • Si le composant Virtualize est placé à l’intérieur d’un élément qui nécessite un nom de balise enfant spécifique, SpacerElement vous permet d’obtenir ou de définir le nom de la balise d’espaceur de virtualisation.

Pour plus d’informations, consultez les sections suivantes de l’article Virtualisation :

Mises à jour de MouseEventArgs

MovementX et MovementY ont été ajoutés à MouseEventArgs.

Pour plus d’informations, consultez Gestion des événements ASP.NET Core Blazor.

Nouvelle page de chargement Blazor

Le modèle de projet Blazor WebAssembly a une nouvelle interface utilisateur de chargement qui indique la progression du chargement de l’application.

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

Amélioration des diagnostics pour l’authentification dans Blazor WebAssembly

Pour diagnostiquer les problèmes d’authentification dans les applications Blazor WebAssembly, la journalisation détaillée est disponible.

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

Interopérabilité JavaScript sur WebAssembly

L’API d’interopérabilité [JSImport]/[JSExport] JavaScript est un nouveau mécanisme de bas niveau permettant d’utiliser .NET dans Blazor WebAssembly et les applications JavaScript. Avec cette nouvelle fonctionnalité d’interopérabilité JavaScript, vous pouvez appeler du code .NET à partir de JavaScript à l’aide du runtime WebAssembly .NET et appeler la fonctionnalité JavaScript à partir de .NET sans dépendance sur le modèle de composant d’interface utilisateur Blazor.

Pour plus d'informations :

Inscription conditionnelle du fournisseur d’état d’authentification

Avant la publication de .NET 7, AuthenticationStateProvider était inscrit dans le conteneur de service avec AddScoped. Cela rendait difficile le débogage d’applications, en imposant un ordre spécifique d’inscriptions de service lors de la fourniture d’une implémentation personnalisée. En raison des changements du framework interne au fil du temps, il n’est plus nécessaire d’inscrire AuthenticationStateProvider auprès de AddScoped.

Dans le code du développeur, apportez la modification suivante à l’inscription du service de fournisseur d’état d’authentification :

- builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
+ builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Dans l’exemple précédent, ExternalAuthStateProvider est l’implémentation du service du développeur.

Améliorations apportées aux outils de génération de WebAssembly .NET

Nouvelles fonctionnalités de la charge de travail wasm-tools pour .NET 7 qui permettent d’améliorer les performances et de gérer les exceptions :

Pour plus d’informations, consultez Outils de génération ASP.NET Core Blazor WebAssembly et compilation AOT (ahead-of-time).

Blazor Hybrid

URL externes

Une option a été ajoutée pour permettre l’ouverture de pages web externes dans le navigateur.

Pour plus d’informations, consultez Routage et navigation ASP.NET Core Blazor Hybrid.

Sécurité

De nouvelles instructions sont disponibles pour les scénarios de sécurité Blazor Hybrid. Pour plus d’informations, consultez les articles suivants :

Performances

Intergiciel de mise en cache de sortie

La mise en cache de sortie est un nouvel intergiciel qui stocke les réponses d’une application web et les sert à partir d’un cache au lieu de les calculer à chaque fois. La mise en cache de sortie diffère de la mise en cache des réponses des manières suivantes :

  • Le comportement de mise en cache est configurable sur le serveur.
  • Les entrées de cache peuvent être invalidées par programmation.
  • Le verrouillage des ressources atténue le risque de tamponnement du cache et de troupeau tonitruant.
  • La revalidation du cache signifie que le serveur peut retourner un code d’état HTTP 304 Not Modified au lieu d’un corps de réponse mis en cache.
  • Le support de stockage du cache est extensible.

Pour plus d’informations, consultez Vue d’ensemble de la mise en cache et Intergiciel de mise en cache de sortie.

Améliorations de HTTP/3

Cette version :

  • Rend HTTP/3 entièrement pris en charge par ASP.NET Core, la prise en charge n’est plus expérimentale.
  • Améliore la prise en charge de Kestrel pour HTTP/3. Les deux principaux domaines d’amélioration sont la parité des fonctionnalités avec HTTP/1.1 et HTTP/2 et les performances.
  • Fournit une prise en charge complète de UseHttps(ListenOptions, X509Certificate2) avec HTTP/3. Kestrel offre des options avancées pour la configuration des certificats de connexion, comme le raccordement à l’indication du nom du serveur (SNI).
  • Ajoute la prise en charge de HTTP/3 sur HTTP.sys et IIS.

L’exemple suivant montre comment utiliser un rappel SNI pour résoudre les options TLS :

using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(8080, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps(new TlsHandshakeCallbackOptions
        {
            OnConnection = context =>
            {
                var options = new SslServerAuthenticationOptions
                {
                    ServerCertificate = 
                         MyResolveCertForHost(context.ClientHelloInfo.ServerName)
                };
                return new ValueTask<SslServerAuthenticationOptions>(options);
            },
        });
    });
});

Un travail important a été effectué dans .NET 7 pour réduire les allocations HTTP/3. Vous pouvez voir certaines de ces améliorations dans les tirages GitHub suivants :

Améliorations des performances de HTTP/2

.NET 7 repense la façon dont Kestrel traite les requêtes HTTP/2. Les applications ASP.NET Core avec des connexions HTTP/2 occupées noteront une utilisation réduite du processeur et un débit plus élevé.

Auparavant, l’implémentation du multiplexage HTTP/2 s’appuyait sur un verrou contrôlant la requête pouvant écrire dans la connexion TCP sous-jacente. Une file d’attente thread-safe remplace le verrou d’écriture. Maintenant, plutôt que de se battre pour déterminer quel thread peut utiliser le verrou d’écriture, les requêtes sont maintenant mises en file d’attente, et un consommateur dédié les traite. Les ressources processeur précédemment gaspillées sont disponibles pour le rest de l’application.

L’un des endroits où ces améliorations peuvent être remarquées est dans gRPC, une infrastructure RPC populaire qui utilise HTTP/2. Les benchmarks Kestrel + gRPC montrent une amélioration spectaculaire :

Performances de diffusion en continu du serveur gRPC

Des modifications ont été apportées au code d’écriture de trame HTTP/2, améliorant les performances lorsque plusieurs flux tentent d’écrire des données sur une seule connexion HTTP/2. Nous répartissons maintenant le travail TLS dans le pool de threads et publions plus rapidement un verrou d’écriture que d’autres flux peuvent acquérir pour écrire leurs données. La réduction des temps d’attente peut entraîner des améliorations significatives des performances dans les cas où il existe une contention pour ce verrou d’écriture. Un benchmark gRPC avec 70 flux sur une seule connexion (avec TLS) a montré une amélioration d’environ 15 % des requêtes par seconde (RPS) avec cette modification.

Prise en charge de WebSockets Http/2

.NET 7 introduit la prise en charge de WebSockets sur HTTP/2 pour Kestrel, le client JavaScript SignalR et SignalR avec Blazor WebAssembly.

L’utilisation de WebSockets sur HTTP/2 tire parti des nouvelles fonctionnalités, dont les suivantes :

  • Compression des en-têtes.
  • Multiplexage, qui réduit le temps et les ressources nécessaires lors de l’envoi de plusieurs demandes au serveur.

Ces fonctionnalités prises en charge sont disponibles dans Kestrel sur toutes les plateformes prenant en charge HTTP/2. La négociation de version étant automatique dans les navigateurs et Kestrel, aucune nouvelle API n’est nécessaire.

Pour plus d’informations, consultez Prise en charge de WebSockets Http/2.

Améliorations des performances de Kestrel sur les machines à nombreux cœurs

Kestrel utilise ConcurrentQueue<T> à de nombreuses fins. L’un des objectifs est de planifier les opérations d’E/S dans le transport de socket par défaut de Kestrel. Le partitionnement de la ConcurrentQueue en fonction du socket associé réduit la contention et augmente le débit sur les machines avec de nombreux cœurs de processeur.

Le profilage sur des machines à nombreux cœurs sur .NET 6 a montré une contention significative dans l’une des autres instances ConcurrentQueue de Kestrel, le PinnedMemoryPool que Kestrel utilise pour mettre en cache des mémoires tampons d’octets.

Dans .NET 7, le pool de mémoire de Kestrel est partitionné de la même façon que sa file d’attente d’E/S, ce qui entraîne une contention beaucoup plus faible et un débit plus élevé sur les machines à nombreux cœurs. Sur les machines virtuelles ARM64 à 80 cœurs, nous constatons une amélioration de plus de 500 % des réponses par seconde (RPS) dans le benchmark en texte clair TechEmpower. Sur les machines virtuelles AMD à 48 cœurs, l’amélioration est de près de 100 % dans notre benchmark JSON HTTPS.

Événement ServerReady pour mesurer le temps de démarrage

Les applications utilisant EventSource peuvent mesurer le temps de démarrage pour comprendre et optimiser les performances de démarrage. Le nouvel événement ServerReady dans Microsoft.AspNetCore.Hosting représente le point où le serveur est prêt à répondre aux requêtes.

Serveur

Nouvel événement ServerReady pour mesurer le temps de démarrage

L’événement ServerReady a été ajouté pour mesurer le temps de démarrage des applications ASP.NET Core.

IIS

Cliché instantané dans IIS

La prise de clichés instantanés des assemblys d’application dans le module ASP.NET Core (ANCM) pour IIS peut offrir une meilleure expérience utilisateur que l’arrêt de l’application en déployant un fichier App Offline.

Pour plus d’informations, consultez Clichés instantanés dans IIS.

Divers

Améliorations de la chaîne de certificats complète de Kestrel

HttpsConnectionAdapterOptions offre une nouvelle propriété ServerCertificateChain de type X509Certificate2Collection, qui facilite la validation des chaînes de certificats en autorisant la spécification d’une chaîne complète incluant des certificats intermédiaires. Pour plus d’informations, consultez dotnet/aspnetcore#21513.

dotnet watch run

Sortie de console améliorée pour dotnet watch

La sortie de console de dotnet watch a été améliorée pour mieux s’aligner sur la journalisation d’ASP.NET Core et pour se distinguer avec des 😮emojis😍.

Voici un exemple de ce à quoi ressemble la nouvelle sortie :

sortie pour dotnet watch

Pour plus d’informations, consultez cette requête de tirage GitHub.

Configurer dotnet watch pour qu’il redémarre toujours pour les modifications non applicables

Les modifications non applicables sont des modifications qui ne peuvent pas être rechargées à chaud. Pour configurer dotnet watch pour qu’il redémarre toujours sans invite de modifications non applicables, définissez la variable d’environnement DOTNET_WATCH_RESTART_ON_RUDE_EDIT sur true.

Mode sombre pour la page d’exceptions du développeur

La prise en charge du mode sombre a été ajoutée à la page d’exceptions du développeur, grâce à une contribution de Patrick Westerhoff. Pour tester le mode sombre dans un navigateur, à partir de la page Outils de développement, définissez le mode sur Sombre. Par exemple, dans Firefox :

Outils F12 mode sombre FF

Dans Chrome :

Outils F12 Mode sombre Chrome

Option de modèle de projet permettant d’utiliser la méthode Program.Main au lieu d’instructions de niveau supérieur

Les modèles .NET 7 incluent une option permettant de ne pas utiliser d’instructions de niveau supérieur et de générer un namespace et une méthode Main déclarée sur une classe Program.

À l’aide de l’interface CLI .NET, utilisez l’option --use-program-main :

dotnet new web --use-program-main

Avec Visual Studio, cochez la nouvelle case Ne pas utiliser d’instructions de niveau supérieur lors de la création du projet :

case à cocher

Modèles Angular et React mis à jour

Le modèle de projet Angular a été mis à jour vers Angular 14. Le modèle de projet React a été mis à jour vers React 18.2.

Gérer les jetons web JSON dans le développement avec dotnet user-jwts

Le nouvel outil en ligne de commande dotnet user-jwts peut créer et gérer des jetons web JSON (JWT) locaux spécifiques à l’application. Pour plus d’informations, consultez Gérer les jetons web JSON en développement avec dotnet user-jwts.

Prise en charge des en-têtes de requête supplémentaires dans W3CLogger

Vous pouvez maintenant spécifier des en-têtes de requête supplémentaires à consigner lors de l’utilisation de l’enregistreur d’événements W3C en appelant AdditionalRequestHeaders() sur W3CLoggerOptions :

services.AddW3CLogging(logging =>
{
    logging.AdditionalRequestHeaders.Add("x-forwarded-for");
    logging.AdditionalRequestHeaders.Add("x-client-ssl-protocol");
});

Pour plus d’informations, consultez Options W3CLogger.

Décompression des requêtes

Nouvel Intergiciel de décompression de requête :

  • Permet aux points de terminaison d’API d’accepter les requêtes avec du contenu compressé.
  • Utilise l’en-tête HTTP Content-Encoding pour identifier et décompresser automatiquement les requêtes qui contiennent du contenu compressé.
  • Élimine la nécessité d’écrire du code pour gérer les requêtes compressées.

Pour plus d’informations, consultez Intergiciel de décompression de requête.