Compartilhar via


Novidades do ASP.NET Core 7.0

Este artigo destaca as alterações mais significativas no ASP.NET Core 7.0, com links para a documentação relevante.

Middleware de limitação de taxa no ASP.NET Core

O middleware Microsoft.AspNetCore.RateLimiting fornece middleware de limitação de taxa. Os aplicativos configuram políticas de limitação de taxa e anexam as políticas aos pontos de extremidade. Para obter mais informações, confira Middleware de limitação de taxa no ASP.NET Core.

A autenticação usa um único esquema como DefaultScheme

Como parte do trabalho para simplificar a autenticação, quando há apenas um único esquema de autenticação registrado, ele é usado automaticamente como o DefaultScheme e não precisa ser especificado. Para obter mais informações, confira DefaultScheme.

MVC e Razor Pages

Suporte a modelos anuláveis em exibições de MVC e Razor Pages

Há suporte a modelos de página ou exibição anuláveis para melhorar a experiência no uso da verificação de estado nulo com aplicativos do ASP.NET Core:

@model Product?

Associar com IParsable<T>.TryParse em controladores de API e MVC

A API IParsable<TSelf>.TryParse dá suporte a valores de parâmetro de ação do controlador de associação. Para obter mais informações, confira Associar com IParsable<T>.TryParse.

Em versões do ASP.NET Core anteriores a 7, a validação de consentimento de cookie usa o valor de cookieyes para indicar consentimento. Agora você pode especificar o valor que representa o consentimento. Por exemplo, você pode usar true em vez 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();

Para obter mais informações, confira Personalizar o valor de consentimento de cookie.

Controladores de API

Associação de parâmetros com DI em controladores de API

A associação de parâmetros para ações de controlador de API vincula parâmetros por meio de injeção de dependência quando o tipo é configurado como um serviço. Isso significa que não é mais necessário aplicar explicitamente o atributo [FromServices] a um parâmetro. No código a seguir, ambas as ações retornam a hora:

[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);
}

Em casos raros, a DI automática pode interromper aplicativos que têm um tipo de DI que também é aceito em um método de ação de um controlador de API. Não é comum ter um tipo na DI e como argumento em uma ação do controlador da API. Para desabilitar a associação automática de parâmetros, defina 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();

No ASP.NET Core 7.0, os tipos na DI são verificados na inicialização do aplicativo com IServiceProviderIsService para determinar se um argumento em uma ação do controlador de API vem da DI ou de outras fontes.

O novo mecanismo para inferir a origem da vinculação dos parâmetros de ação do API Controller usa as seguintes regras:

  1. Um BindingInfo.BindingSource especificado anteriormente nunca é substituído.
  2. Um parâmetro de tipo complexo, registrado no contêiner DI, é atribuído BindingSource.Services.
  3. Um parâmetro de tipo complexo, não é registrado no contêiner DI, é atribuído BindingSource.Body.
  4. Um parâmetro com um nome que aparece como um valor de rota em qualquer modelo de rota é atribuído BindingSource.Path.
  5. Todos os outros parâmetros são BindingSource.Query.

Nomes de propriedade JSON em erros de validação

Por padrão, quando ocorre um erro de validação, a validação de modelo produz um ModelStateDictionary com o nome da propriedade como a chave de erro. Alguns aplicativos, como aplicativos de página única, se beneficiam do uso de nomes de propriedade JSON para erros de validação gerados de APIs Web. O código abaixo configura a validação para usar os nomes de SystemTextJsonValidationMetadataProvider a fim de usar nomes de propriedade JSON:

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();

O código abaixo configura a validação para usar NewtonsoftJsonValidationMetadataProvider a fim de usar o nome da propriedade JSON ao usar 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();

Para obter mais informações, confira Usar nomes de propriedade JSON em erros de validação

APIs mínimas

Filtros em aplicativos de API mínima

Filtros mínimos de API permitem que os desenvolvedores implementem a lógica de negócios que dá suporte a:

  • Execução do código antes e depois do manipulador de rotas.
  • Inspeção e modificação de parâmetros fornecidos durante uma invocação do manipulador de rotas.
  • Interceptação do comportamento de resposta de um manipulador de rotas.

Os filtros podem ser úteis nos seguintes cenários:

  • Validação dos parâmetros de solicitação e do corpo que são enviados a um ponto de extremidade.
  • Registro em log de informações sobre a solicitação e a resposta.
  • Validação se uma solicitação está buscando uma versão de API com suporte.

Para obter mais informações, confira Filtros em aplicativos de API mínima

Associar matrizes e valores de cadeia de caracteres com base em cabeçalhos e cadeias de caracteres de consulta

No ASP.NET 7, há suporte à associação de cadeias de caracteres de consulta a uma matriz de tipos primitivos, matrizes de cadeia de caracteres e StringValues:

// 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]}");

Há suporte à associação de cadeias de caracteres de consulta ou valores de cabeçalho a uma matriz de tipos complexos quando o tipo tem TryParse implementado. Para obter mais informações, confira Associar matrizes e valores de cadeia de caracteres com base em cabeçalhos e cadeias de caracteres de consulta.

Para obter mais informações, confira Adicionar resumo ou descrição do ponto de extremidade.

Associar o corpo da solicitação como um Stream ou PipeReader

O corpo da solicitação pode ser associado como um Stream ou PipeReader para dar o suporte necessário a cenários em que o usuário precisa processar dados e:

  • Armazenar os dados no armazenamento de blobs ou enfileirar os dados para um provedor de fila.
  • Processar os dados armazenados com um processo de trabalho ou uma função de nuvem.

Por exemplo, os dados podem ser enfileirados no Armazenamento de Filas do Azure ou armazenados no Armazenamento de Blobs do Azure.

Para obter mais informações, confira Associar o corpo da solicitação como um Stream ou PipeReader

Novas sobrecargas de Results.Stream

Apresentamos novas sobrecargas de Results.Stream para acomodar cenários que precisam de acesso ao fluxo de resposta HTTP subjacente sem buffer. Essas sobrecargas também melhoram casos em que uma API transmite dados ao fluxo de resposta HTTP, como no Armazenamento de Blobs do Azure. O exemplo a seguir usa ImageSharp para retornar um tamanho reduzido da imagem especificada:

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);
}

Para obter mais informações, confira Exemplos do Stream

Resultados tipados para APIs mínimas

No .NET 6, a interface IResult foi apresentada para representar valores retornados de APIs mínimas que não utilizam o suporte implícito a JSON, serializando o objeto retornado para a resposta HTTP. A classe estática Results é usada para criar objetos IResult variados que representam diferentes tipos de respostas. Por exemplo, definir o código de status de resposta ou redirecionar para outra URL. No entanto IResult, os tipos de estrutura de implementação retornados desses métodos eram internos, dificultando a verificação do tipo específico de IResult retornado dos métodos em um teste de unidade.

No .NET 7, os tipos de implementação IResult são públicos, permitindo declarações de tipo durante o teste. Por exemplo:

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

Melhor capacidade de teste de unidade para manipuladores de rotas mínimos

Os tipos de implementação IResult agora estão disponíveis publicamente no namespace Microsoft.AspNetCore.Http.HttpResults. Os tipos de implementação IResult podem ser usados para testar manipuladores de rota mínimos de unidade ao usar métodos nomeados em vez de lambdas.

O código abaixo usa a 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);
}

Para obter mais informações, confira Tipos de implementação IResult.

Novas interfaces HttpResult

As interfaces abaixo no namespace Microsoft.AspNetCore.Http fornecem uma maneira de detectar o tipo IResult em runtime, que é um padrão comum em implementações de filtro:

Para obter mais informações, confira Interfaces IHttpResult.

Melhorias do OpenAPI para APIs mínimas

Pacote NuGet Microsoft.AspNetCore.OpenApi

O pacote Microsoft.AspNetCore.OpenApi permite interações com especificações OpenAPI em pontos de extremidade. O pacote atua como um link entre os modelos OpenAPI definidos no pacote Microsoft.AspNetCore.OpenApi e os pontos de extremidade definidos em APIs Mínimas. O pacote fornece uma API que examina parâmetros, respostas e metadados de um ponto de extremidade para construir um tipo de anotação OpenAPI usado para descrever um ponto de extremidade.

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();

Chamar WithOpenApi com parâmetros

O método WithOpenApi aceita uma função que pode ser usada para modificar a anotação OpenAPI. Por exemplo, no código a seguir, uma descrição é adicionada ao primeiro parâmetro do ponto de extremidade:

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;
});

Fornecer descrições e resumos de ponto de extremidade

As APIs mínimas agora dão suporte à anotação de operações com descrições e resumos para a geração de especificações da OpenAPI. Você pode chamar métodos de extensão WithDescription e WithSummary ou usar atributos [EndpointDescription] e [EndpointSummary]).

Para obter mais informações, confira OpenAPI em aplicativos de API mínima

Uploads de arquivo usando IFormFile e IFormFileCollection

APIs Mínimas agora dão suporte ao upload de arquivo com IFormFile e IFormFileCollection. O código a seguir usa IFormFile e IFormFileCollection para carregar o arquivo:

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();

Há suporte a solicitações de upload de arquivos autenticados com o uso de um cabeçalho de autorização, um certificado de cliente ou um cabeçalho de cookie.

Não há suporte interno contra a falsificação. No entanto, ele pode ser implementado usando o serviço IAntiforgery.

O atributo [AsParameters] habilita a associação de parâmetros para listas de argumentos

Os atributos [AsParameters] habilita a associação de parâmetros para listas de argumentos. Para obter mais informações, confira Associação de parâmetros para listas de argumentos com [AsParameters].

APIs Mínimas e controladores de API

Novo serviço de detalhes do problema

O serviço de detalhes do problema implementa a interface IProblemDetailsService, que dá suporte à criação de Detalhes do Problema para APIs HTTP.

Para obter mais informações, confira Serviço de detalhes do problema.

Grupos de rotas

O método de extensão MapGroup ajuda a organizar grupos de pontos de extremidade com um prefixo comum. Isso reduz o código repetitivo e permite personalizar grupos inteiros de pontos de extremidade com uma única chamada a métodos como RequireAuthorization e WithMetadata, que adicionam os metadados de ponto de extremidade.

Por exemplo, o código a seguir cria dois grupos de pontos de extremidade semelhantes:

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;
}

Nesse cenário, você pode usar um endereço relativo para o cabeçalho Location no resultado 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);
}

O primeiro grupo de pontos de extremidade corresponderá apenas às solicitações prefixadas com /public/todos e estará acessível sem autenticação. O segundo grupo de pontos de extremidade corresponderá apenas às solicitações prefixadas com /private/todos e exigirá autenticação.

O QueryPrivateTodosalocador de filtros de ponto de extremidade é uma função local que modifica os parâmetros TodoDb do manipulador de rotas para permitir o acesso e o armazenamento de dados todo privados.

Os grupos de rotas também permitem grupos aninhados e padrões de prefixo complexos com parâmetros de rota e restrições. No exemplo a seguir, o manipulador de rotas mapeado para o grupo user pode capturar os parâmetros de rota {org} e {group} definidos nos prefixos do grupo externo.

O prefixo também pode estar vazio. Isso pode ser útil para adicionar metadados ou filtros de ponto de extremidade a um grupo de pontos de extremidade, sem alterar o padrão de rota.

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

Adicionar filtros ou metadados a um grupo é igual a adicioná-los individualmente a cada ponto de extremidade, antes de adicionar filtros ou metadados extras que possam ter sido adicionados a um grupo interno ou ponto de extremidade específico.

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);
});

No exemplo acima, o filtro externo registrará a solicitação de entrada antes do filtro interno, mesmo que tenha sido adicionado depois. Como os filtros foram aplicados a grupos diferentes, a ordem em que foram adicionados em relação uns aos outros não importa. A ordem em que os filtros são adicionados importa se aplicados ao mesmo grupo ou ponto de extremidade específico.

Uma solicitação para /outer/inner/ registrará o seguinte:

/outer group filter
/inner group filter
MapGet filter

gRPC

Transcodificação JSON

A transcodificação JSON de gRPC é uma extensão para ASP.NET Core que cria APIs RESTful JSON completas para serviços gRPC. A transcodificação JSON de gRPC permite:

  • Que aplicativos chamem serviços gRPC com conceitos HTTP familiares.
  • Que os aplicativos gRPC do ASP.NET Core deem suporte a APIs gRPC e RESTful JSON sem replicar a funcionalidade.
  • Suporte experimental para gerar OpenAPI de APIs RESTful transcodificadas pela integração com o Swashbuckle.

Para obter mais informações, confira a transcodificação gRPC JSON em aplicativos gRPC do ASP.NET Core e Usar OpenAPI com a gRPC para transcodificar aplicativos JSON ASP.NET Core.

Verificações de integridade de gRPC no ASP.NET Core

O protocolo de verificação de integridade gRPC é um padrão para relatar a integridade de aplicativos de servidor gRPC. Um aplicativo expõe verificações de integridade como um serviço gRPC. Normalmente, eles são usados com um serviço de monitoramento externo para verificar o status de um aplicativo.

O ASP.NET Core gRPC adicionou suporte interno para verificações de integridade de gRPC com o pacote Grpc.AspNetCore.HealthChecks. Os resultados das verificações de integridade do .NET são relatados aos chamadores.

Para obter mais informações, confira Verificações de integridade de gRPC no ASP.NET Core.

Suporte a credenciais de chamada aprimoradas

As credenciais de chamada são a maneira recomendada de configurar um cliente gRPC a fim de enviar um token de autenticação para o servidor. Os clientes gRPC dão suporte a dois novos recursos para facilitar o uso das credenciais de chamada:

  • Suporte a credenciais de chamada com conexões de texto sem formatação. Anteriormente, uma chamada gRPC só enviava credenciais de chamada se a conexão fosse protegida com TLS. Uma nova configuração em GrpcChannelOptions, chamada UnsafeUseInsecureChannelCallCredentials, permite que esse comportamento seja personalizado. Há implicações de segurança pela não proteção de uma conexão com o TLS.
  • Um novo método chamado AddCallCredentials está disponível com o factory de clientes gRPC. AddCallCredentials é uma maneira rápida de configurar credenciais de chamada para um cliente gRPC e integra-se bem com a DI (injeção de dependência).

O código abaixo configura a factory de clientes gRPC para enviar metadados de 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;
    });

Para obter mais informações, confira Configurar um token de portador com a factory de clientes gRPC.

SignalR

Resultados do cliente

O servidor agora dá suporte à solicitação de um resultado de um cliente. Isso requer que o servidor use ISingleClientProxy.InvokeAsync e o cliente retorne um resultado de seu manipulador .On. Hubs fortemente tipado também podem retornar valores de métodos de interface.

Para obter mais informações, confira Resultados do cliente

Injeção de dependência para métodos de hub SignalR

Agora, os métodos hub SignalR dão suporte à injeção de serviços por meio da DI (injeção de dependência).

Os construtores de hub podem aceitar serviços de DI como parâmetros, que podem ser armazenados em propriedades na classe para uso em um método de hub. Para obter mais informações, confira Injetar serviços em um hub

Blazor

Manipular eventos de alteração de local e estado de navegação

No .NET 7, o Blazor dá suporte a eventos de alteração de local e manutenção do estado de navegação. Isso permite que você alerte os usuários sobre trabalho não salvo ou execute ações relacionadas quando o usuário faz uma navegação de página.

Para obter mais informações, confira as seções abaixo do artigo Roteamento e navegação:

Modelos de projeto do Blazor vazios

O Blazor tem dois novos modelos de projeto para começar do zero. Os novos modelos de projeto Blazor Server App Empty e Blazor WebAssembly App Empty são exatamente como seus equivalentes não vazios, mas sem código de exemplo. Esses modelos vazios incluem apenas uma home page básica, e removemos o Bootstrap para que você possa começar com uma estrutura CSS diferente.

Para obter mais informações, consulte os seguintes artigos:

Elementos personalizados de Blazor

O pacote Microsoft.AspNetCore.Components.CustomElements permite a criação de elementos DOM personalizados usando o Blazor.

Para saber mais, confira Componentes Razor do ASP.NET Core.

Modificadores de associação (@bind:after, @bind:get, @bind:set)

Importante

Os recursos @bind:after/@bind:get/@bind:set estão recebendo atualizações adicionais no momento. Para aproveitar as atualizações mais recentes, confirme se você instalou o SDK mais recente.

Não há suporte ao uso de um parâmetro de retorno de chamada de evento ([Parameter] public EventCallback<string> ValueChanged { get; set; }). Em vez disso, transmita um método que retorna Action ou Task para /@bind:set@bind:after.

Para saber mais, consulte os recursos a seguir:

No .NET 7, você pode executar a lógica assíncrona após a conclusão de um evento de associação usando o novo modificador @bind:after. No exemplo a seguir, o método assíncrono PerformSearch é executado automaticamente após uma alteração no texto de pesquisa ser detectada:

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

@code {
    private string searchText;

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

No .NET 7, também é mais fácil configurar a associação para parâmetros de componente. Os componentes podem dar suporte à associação de dados bidirecional definindo um par de parâmetros:

  • @bind:get: especifica o valor a ser associado.
  • @bind:set: especifica um retorno de chamada para quando o valor é alterado.

Os modificadores @bind:get e @bind:set são sempre usados juntos.

Exemplos:

@* 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; }
}

Para obter mais informações sobre o componente InputText, confira Componentes de entrada do ASP.NET Core Blazor.

Aprimoramentos de Recarga Dinâmica

No .NET 7, o suporte à Recarga Dinâmica inclui o seguinte:

  • Os componentes redefinem os parâmetros com os valores padrão quando um valor é removido.
  • Blazor WebAssembly:
    • Adição de novos tipos.
    • Adição de classes aninhadas.
    • Adição de métodos estáticos e de instância aos tipos existentes.
    • Adição de campos e métodos estáticos aos tipos existentes.
    • Adição de lambdas estáticos aos métodos existentes.
    • Adicione lambdas que capturam this em métodos existentes que já capturaram this anteriormente.

Solicitações de autenticação dinâmica com MSAL no Blazor WebAssembly

Novidade no .NET 7, o Blazor WebAssembly dá suporte à criação de solicitações de autenticação dinâmica no runtime com parâmetros personalizados para lidar com cenários avançados de autenticação.

Para obter mais informações, consulte os seguintes artigos:

Melhorias na depuração do Blazor WebAssembly

A depuração do Blazor WebAssembly tem as seguintes melhorias:

  • Suporte à configuração Apenas Meu Código para mostrar ou ocultar membros do tipo que não são do código do usuário.
  • Suporte à inspeção de matrizes multidimensionais.
  • A Pilha de Chamadas agora mostra o nome correto de métodos assíncronos.
  • Avaliação de expressão aprimorada.
  • Manipulação correta da palavra-chave new em membros derivados.
  • Suporte a atributos relacionados ao depurador em System.Diagnostics.

Suporte a System.Security.Cryptography no WebAssembly

O .NET 6 deu suporte à família SHA de algoritmos de hash durante a execução no WebAssembly. O .NET 7 permite mais algoritmos criptográficos com o aproveitamento do SubtleCrypto, sempre que possível, e com a reversão para uma implementação do .NET quando SubtleCrypto não pode ser usado. Os algoritmos abaixo têm suporte pelo WebAssembly no .NET 7:

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

Para obter mais informações, confira Os desenvolvedores voltados para browser-wasm podem usar APIs de Criptografia da Web (dotnet/runtime #40074).

Injetar serviços em atributos de validação personalizados

Agora você pode injetar serviços em atributos de validação personalizados. Blazor configura o ValidationContext para que ele possa ser usado como um provedor de serviços.

Para obter mais informações, consulte Validação de formulários Blazor do ASP.NET Core.

Componentes Input* fora de um EditContext/EditForm

Os componentes de entrada internos agora têm suporte fora de um formulário na marcação de componente Razor.

Para obter mais informações, consulte Componentes de entrada Blazor do ASP.NET Core.

Alterações no modelo de projeto

Quando o .NET 6 foi lançado no ano passado, a marcação HTML da página _Host (Pages/_Host.chstml) foi dividida entre a página _Host e uma nova página _Layout (Pages/_Layout.chstml) no modelo de projeto do Blazor Server do .NET 6.

No .NET 7, a marcação HTML foi recombinada com a página _Host em modelos de projeto.

Várias alterações adicionais foram feitas nos modelos de projeto do Blazor. Não é viável listar todas as alterações nos modelos na documentação. Para migrar um aplicativo para o .NET 7 e adotar todas as alterações, confira Migrar de ASP.NET Core 6.0 para 7.0.

Componente QuickGrid experimental

O novo componente QuickGrid fornece um componente de grade de dados conveniente para os requisitos mais comuns e como uma arquitetura de referência e linha de base de desempenho para qualquer pessoa que crie componentes Blazor de grade de dados.

Para saber mais, confira Componente QuickGrid do Blazor no ASP.NET Core.

Demonstração ao vivo: QuickGrid para aplicativo de exemplo do Blazor

Aprimoramentos de virtualização

Aprimoramentos de virtualização no .NET 7:

  • O componente Virtualize dá suporte ao uso do documento em si como a raiz de rolagem, como uma alternativa para ter algum outro elemento com overflow-y: scroll aplicado.
  • Se o componente Virtualize for colocado dentro de um elemento que requer um nome de tag filho específico, SpacerElement permite que você obtenha ou defina o nome da tag do espaçador de virtualização.

Para obter mais informações, confira as seguintes seções do artigo Virtualização:

Atualizações de MouseEventArgs

MovementX e MovementY foram adicionados a MouseEventArgs.

Para obter mais informações, confira Tratamento de eventos do Blazor no ASP.NET Core.

Nova página de carregamento do Blazor

O modelo de projeto do Blazor WebAssembly tem uma nova interface do usuário de carregamento que mostra o progresso do carregamento do aplicativo.

Para obter mais informações, confira Inicialização Blazor do ASP.NET Core.

Diagnóstico aprimorado para autenticação no Blazor WebAssembly

O log detalhado está disponível para ajudar a diagnosticar problemas de autenticação em aplicativos Blazor WebAssembly.

Para obter mais informações, confira Registro em log do Blazor do ASP.NET Core.

Interoperabilidade do JavaScript no WebAssembly

A API de interoperabilidade do JavaScript [JSImport]/[JSExport] é um novo mecanismo de baixo nível para usar o .NET em aplicativos baseados em Blazor WebAssembly e JavaScript. Com essa nova funcionalidade de interoperabilidade do JavaScript, você pode invocar o código .NET do JavaScript usando o runtime do .NET WebAssembly e chamar a funcionalidade JavaScript do .NET sem nenhuma dependência do modelo de componente da interface do usuário do Blazor.

Para obter mais informações:

Registro condicional do provedor de estado de autenticação

Antes da versão do .NET 7, AuthenticationStateProvider era registrado no contêiner de serviço em AddScoped. Isso dificultava a depuração de aplicativos, pois forçava uma ordem específica de registros de serviço ao fornecer uma implementação personalizada. Devido a alterações na estrutura interna ao longo do tempo, não é mais necessário registrar AuthenticationStateProvider em AddScoped.

No código do desenvolvedor, faça a seguinte alteração no registro do serviço do provedor de estado de autenticação:

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

No exemplo anterior, ExternalAuthStateProvider é a implementação de serviço do desenvolvedor.

Melhorias nas ferramentas de build do .NET WebAssembly

Novos recursos na carga de trabalho wasm-tools do .NET 7 que ajudam a melhorar o desempenho e lidar com exceções:

Para obter mais informações, confira Ferramentas de criação e compilação antecipada (AOT) do Blazor WebAssembly no ASP.NET Core.

Blazor Hybrid

URLs externas

Foi adicionada uma opção que permite abrir páginas da Web externas no navegador.

Para obter mais informações, confira Roteamento e navegação do Blazor Hybrid no ASP.NET Core.

Segurança

Novas diretrizes estão disponíveis para cenários de segurança do Blazor Hybrid. Para obter mais informações, consulte os seguintes artigos:

Desempenho

Middleware de cache de saída

O cache de saída é um novo middleware que armazena respostas de um aplicativo Web e as serve de um cache em vez de sempre computá-las. O cache de saída difere do cache de resposta das seguintes maneiras:

  • O comportamento de cache pode ser configurado no servidor.
  • As entradas de cache podem ser invalidadas programaticamente.
  • O bloqueio de recursos reduz o risco de debandada de cache e rebanho estrondoso.
  • A revalidação de cache significa que o servidor pode retornar um código de status HTTP 304 Not Modified em vez de um corpo de resposta armazenado em cache.
  • O meio de armazenamento de cache é extensível.

Para obter mais informações, confira Visão geral do cache e Middleware de cache de saída.

Melhorias de HTTP/3

Esta versão:

  • Torna o HTTP/3 totalmente compatível com o ASP.NET Core; ele não é mais experimental.
  • Melhora o suporte do Kestrel a HTTP/3. As duas principais áreas de melhoria são a paridade de recursos com HTTP/1.1 e HTTP/2 e o desempenho.
  • Fornece suporte completo a UseHttps(ListenOptions, X509Certificate2) com HTTP/3. O Kestrel oferece opções avançadas para configurar certificados de conexão, por exemplo, conectar-se à SNI (Indicação de Nome de Servidor).
  • Adiciona suporte a HTTP/3 em HTTP.sys e IIS.

O exemplo abaixo mostra como usar uma SNI de retorno de chamada para resolver opções de 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);
            },
        });
    });
});

Foi feito um trabalho significativo no .NET 7 para reduzir as alocações de HTTP/3. Você pode ver algumas dessas melhorias nas seguintes PRs do GitHub:

Melhorias de desempenho HTTP/2

O .NET 7 apresenta uma nova arquitetura significativa de como o Kestrel processa solicitações HTTP/2. Os aplicativos ASP.NET Core com conexões HTTP/2 ocupadas terão um uso reduzido da CPU e uma maior taxa de transferência.

Anteriormente, a implementação de multiplexação HTTP/2 dependia de um bloqueio que controlava qual solicitação podia gravar na conexão TCP subjacente. Uma fila thread-safe substitui o bloqueio de gravação. Agora, em vez de brigar em relação a qual thread pode usar o bloqueio de gravação, as solicitações agora fazem fila e um consumidor dedicado as processa. Os recursos de CPU desperdiçados anteriormente ficam disponíveis para o restante do aplicativo.

Um lugar em que essas melhorias podem ser observadas é em gRPC, uma estrutura RPC popular que usa HTTP/2. Os parâmetros de comparação de Kestrel + gRPC mostram uma melhora drástica:

Desempenho de streaming do servidor gRPC

Foram feitas alterações no código de gravação de quadro HTTP/2 que melhora o desempenho quando há vários fluxos tentando gravar dados em uma única conexão HTTP/2. Agora, expedimos o trabalho do TLS para o pool de threads e liberamos mais rapidamente um bloqueio de gravação que outros fluxos podem adquirir para gravar seus dados. A redução dos tempos de espera pode produzir melhorias significativas no desempenho nos casos em que há disputa por esse bloqueio de gravação. Um parâmetro de comparação gRPC com 70 fluxos em uma única conexão (com TLS) mostrou uma melhoria de aproximadamente 15% nas solicitações por segundo (RPS) com essa alteração.

Suporte a WebSockets HTTP/2

O .NET 7 apresenta suporte do Websockets por HTTP/2 a Kestrel, cliente JavaScript SignalRe SignalR com Blazor WebAssembly.

O uso de WebSockets por HTTP/2 aproveita novos recursos, como:

  • Compactação de cabeçalho.
  • Multiplexação, que reduz o tempo e os recursos necessários ao fazer várias solicitações ao servidor.

Esses recursos com suporte estão disponíveis no Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e no Kestrel, ou seja, nenhuma nova API é necessária.

Para obter mais informações, confira Suporte a WebSockets HTTP/2.

Aprimoramentos de desempenho do Kestrel em computadores de muitos núcleos

O Kestrel usa ConcurrentQueue<T> para muitas finalidades. Uma delas é agendar operações de E/S no transporte do soquete padrão do Kestrel. O particionamento da ConcurrentQueue com base no soquete associado reduz a disputa e aumenta a taxa de transferência em computadores com muitos núcleos de CPU.

A criação de perfil em computadores com muitos núcleos no .NET 6 mostrou uma disputa significativa em uma outras instâncias ConcurrentQueue do Kestrel, a PinnedMemoryPool que o Kestrel usa para armazenar em cache buffers de bytes.

No .NET 7, o pool de memória do Kestrel é particionado da mesma forma que sua fila de E/S, o que leva a uma disputa muito menor e a uma maior taxa de transferência em computadores com muitos núcleos. Nas VMs ARM64 de 80 núcleos, estamos vendo uma melhoria de mais de 500% nas RPS (respostas por segundo) no parâmetro de comparação de texto sem formatação TechEmpower. Em VMs AMD de 48 Núcleos, a melhoria é de quase 100% em nosso parâmetro de comparação HTTPS JSON.

Evento ServerReady para medir o tempo de inicialização

Os aplicativos que usam EventSource podem medir o tempo de inicialização para entender e otimizar o desempenho da inicialização. O novo evento ServerReady em Microsoft.AspNetCore.Hosting representa o ponto em que o servidor está pronto para responder às solicitações.

Servidor

Novo evento ServerReady para medir o tempo de inicialização

O evento ServerReady foi adicionado para medir o tempo de inicialização dos aplicativos do ASP.NET Core.

IIS

Cópia de sombra no IIS

A cópia de sombra dos assemblies de aplicativo para o ANCM (Módulo Principal do ASP.NET) para IIS podem fornecer uma melhor experiência do usuário final do que a interrupção do aplicativo com a implantação de um arquivo offline do aplicativo.

Para obter mais informações, confira Cópia de Sombra no IIS.

Diversos

Aprimoramentos completos da cadeia de certificados Kestrel

HttpsConnectionAdapterOptions tem uma nova propriedade ServerCertificateChain do tipo X509Certificate2Collection, o que facilita a validação das cadeias de certificados, permitindo que uma cadeia completa, incluindo certificados intermediários, seja especificada. Confira dotnet/aspnetcore#21513 para obter mais detalhes.

dotnet watch

Saída de console aprimorada para o dotnet watch

A saída do console do dotnet watch foi aprimorada para se alinhar melhor ao registro em log do ASP.NET Core e se destacar com 😮emojis😍.

Aqui está um exemplo da aparência da nova saída:

saída do dotnet watch

Saiba mais nesta solicitação de pull do GitHub.

Configurar o dotnet watch para sempre reiniciar em caso de edições rudes

Edições rudes são edições que não podem ser recarregadas dinamicamente. Para configurar o dotnet watch e sempre reiniciar sem um prompt de edições rudes, defina a variável de ambiente DOTNET_WATCH_RESTART_ON_RUDE_EDIT como true.

Modo escuro da página de exceção do desenvolvedor

O suporte ao modo escuro foi adicionado à página de exceção do desenvolvedor, graças a uma contribuição de Patrick Westerhoff. Para testar o modo escuro em um navegador, na página de ferramentas do desenvolvedor, defina o modo como escuro. Por exemplo, no Firefox:

Modo escuro FF das ferramentas F12

No Chrome:

Modo escuro do Chrome das ferramentas F12

Opção de modelo de projeto para usar o método Program.Main em vez de instruções de nível superior

Os modelos do .NET 7 incluem uma opção para não usar instruções de nível superior e gerar um e um namespace e um método Main declarado em uma classe Program.

Usando a CLI do .NET, use a opção --use-program-main:

dotnet new web --use-program-main

Com o Visual Studio, marque a nova caixa de seleção Não usar instruções de nível superior durante a criação do projeto:

checkbox

Modelos de Angular e React atualizados

O modelo de projeto Angular foi atualizado para Angular 14. O modelo de projeto do React foi atualizado para o React 18.2.

Gerenciar Tokens Web JSON em desenvolvimento com dotnet user-jwts

A nova ferramenta de linha de comando dotnet user-jwts pode criar e gerenciar JWTs (Tokens Web JSON) locais específicos do aplicativo. Para obter mais informações, confira Gerenciar Tokens Web JSON em desenvolvimento com dotnet user-jwts.

Suporte a cabeçalhos de solicitação adicionais no W3CLogger

Agora você pode especificar cabeçalhos de solicitação adicionais para fazer logon quando usa o agente W3C chamando AdditionalRequestHeaders() em W3CLoggerOptions:

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

Para obter mais informações, confira Opções do W3CLogger.

Solicitação de descompactação

O novo middleware de descompactação de solicitação:

  • Permite que os pontos de extremidade de API aceitem solicitações com conteúdo compactado.
  • Usa o cabeçalho HTTP Content-Encoding para identificar e descompactar automaticamente solicitações que contêm conteúdo compactado.
  • Elimina a necessidade de escrever código para lidar com solicitações compactadas.

Para obter mais informações, confira Middleware de descompactação de solicitação.