O que há de novo no ASP.NET Core 9.0
Este artigo destaca as alterações mais significativas no ASP.NET Core 9.0 com links para documentação relevante.
Otimização da entrega de ativos estáticos
MapStaticAssets
convenções de roteamento de ponto de extremidade são uma nova funcionalidade que otimiza a entrega de recursos estáticos em aplicações ASP.NET Core.
Para obter informações sobre a distribuição de recursos estáticos para aplicações Blazor, consulte ASP.NET Core Blazor arquivos estáticos.
Seguir as melhores práticas de produção para servir ativos estáticos requer uma quantidade significativa de trabalho e conhecimento técnico. Sem otimizações como compactação, armazenamento em cache e impressões digitais :
- O navegador tem que fazer solicitações adicionais em cada carregamento de página.
- Mais bytes do que o necessário são transferidos através da rede.
- Às vezes, versões obsoletas de arquivos são servidas aos clientes.
A criação de aplicativos Web de alto desempenho requer a otimização da entrega de ativos para o navegador. As otimizações possíveis incluem:
- Sirva um determinado ativo uma vez até que o arquivo seja alterado ou o navegador limpe seu cache. Defina o cabeçalho ETag.
- Impeça que o navegador use ativos antigos ou obsoletos depois que um aplicativo for atualizado. Defina o cabeçalho Última modificação.
- Configure cabeçalhos adequados de cache .
- Use middleware de cache.
- Sirva versões compactadas dos ativos sempre que possível.
- Use um CDN para servir os ativos mais próximos do usuário.
- Minimize o tamanho dos recursos servidos ao navegador. Esta otimização não inclui a minificação.
MapStaticAssets é um novo recurso que otimiza a entrega de ativos estáticos em um aplicativo. Ele foi projetado para funcionar com todas as estruturas de interface do usuário, incluindo Blazor, Razor Pages e MVC. Normalmente, é uma substituição direta para UseStaticFiles
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
+app.MapStaticAssets();
-app.UseStaticFiles();
app.MapRazorPages();
app.Run();
MapStaticAssets
opera combinando processos de compilação e tempo de publicação para coletar informações sobre todos os recursos estáticos em um aplicativo. Essas informações são então utilizadas pela biblioteca de tempo de execução para servir esses ficheiros de forma eficiente ao navegador.
MapStaticAssets
pode substituir UseStaticFiles
na maioria das situações; no entanto, é otimizado para servir os recursos de que o aplicativo tem conhecimento no momento da compilação e publicação. Se a aplicação servir recursos de outros locais, como disco ou recursos incorporados, UseStaticFiles
deve ser usado.
MapStaticAssets
fornece os seguintes benefícios não encontrados com UseStaticFiles
:
- Redução do tempo de construção para todos os recursos na aplicação:
-
gzip
durante o desenvolvimento egzip + brotli
durante a publicação. - Todos os ativos são compactados com o objetivo de reduzir o tamanho dos ativos ao mínimo.
-
-
ETags
baseado em conteúdo: OsEtags
para cada recurso são a Base64 cadeia de caracteres codificada do SHA-256 hash do conteúdo. Isso garante que o navegador só faça o download de um arquivo novamente se seu conteúdo tiver sido alterado.
A tabela a seguir mostra os tamanhos originais e compactados dos arquivos CSS e JS no modelo padrão do Razor Pages:
Ficheiro | Original | Comprimido | % Redução |
---|---|---|---|
bootstrap.min.css | 163 | 17.5 | 89.26% |
jquery.js | 89.6 | 28 | 68,75% |
bootstrap.min.js | 78.5 | 20 | 74,52% |
Total | 331,1 | 65.5 | 80,20% |
A tabela a seguir mostra os tamanhos originais e compactados usando a biblioteca de componentes Fluent UI Blazor:
Ficheiro | Original | Comprimido | Redução % |
---|---|---|---|
fluente.js | 384 | 73 | 80,99% |
fluent.css | 94 | 11 | 88.30% |
Total | 478 | 84 | 82.43% |
Para um total de 478 KB descompactados para 84 KB compactados.
A tabela a seguir mostra os tamanhos originais e comprimidos usando a biblioteca de componentes MudBlazorBlazor:
Ficheiro | Original | Comprimido | Redução |
---|---|---|---|
MudBlazor.min.css | 541 | 37,5 | 93,07% |
MudBlazor.min.js | 47.4 | 9.2 | 80,59% |
Total | 588.4 | 46.7 | 92.07% |
A otimização acontece automaticamente ao usar MapStaticAssets
. Quando uma biblioteca é adicionada ou atualizada, por exemplo, com novo JavaScript ou CSS, os ativos são otimizados como parte da compilação. A otimização é especialmente benéfica para ambientes móveis que podem ter uma largura de banda menor ou conexões não confiáveis.
Para obter mais informações sobre os novos recursos de entrega de arquivos, consulte os seguintes recursos:
Habilitando a compactação dinâmica no servidor vs usando MapStaticAssets
MapStaticAssets
tem as seguintes vantagens sobre a compactação dinâmica no servidor:
- É mais simples porque não há nenhuma configuração específica do servidor.
- É mais eficiente porque os recursos são compactados em tempo de compilação.
- Permite que o desenvolvedor gaste tempo extra durante o processo de compilação para garantir que os ativos tenham o tamanho mínimo.
Considere a tabela a seguir comparando a compactação MudBlazor com a compactação dinâmica do IIS e MapStaticAssets
:
IIS gzip | MapStaticAssets |
Redução MapStaticAssets |
---|---|---|
≅ 90 | 37.5 | 59% |
Blazor
Esta seção descreve as novas funcionalidades para Blazor.
Modelo de solução .NET MAUIBlazor Hybrid e Aplicação Web
Um novo modelo de solução facilita a criação de .NET MAUI aplicativos cliente Web nativos e Blazor que compartilham a mesma interface do usuário. Este modelo mostra como criar aplicativos cliente que maximizam a reutilização de código e visam Android, iOS, Mac, Windows e Web.
Os principais recursos deste modelo incluem:
- A capacidade de escolher um modo de renderização interativo Blazor para o aplicativo Web.
- Criação automática dos projetos apropriados, incluindo um Blazor Web App (renderização automática interativa global) e um aplicativo .NET MAUIBlazor Hybrid.
- Os projetos criados usam uma biblioteca de classes Razor compartilhadas (RCL) para manter os componentes Razor da interface do usuário.
- É incluído um código de exemplo que demonstra como usar a injeção de dependência para fornecer diferentes implementações de interface para o aplicativo Blazor Hybrid e o Blazor Web App.
Para começar, instale o SDK do
dotnet workload install maui
Crie uma solução a partir do modelo de projeto em um shell de comando usando o seguinte comando:
dotnet new maui-blazor-web
O modelo também está disponível no Visual Studio.
Observação
Atualmente, ocorre uma exceção quando diferentes modos de renderização "Blazor" são definidos ao nível de página/componente. Para mais informações, consulte BlazorWebView precisa de uma forma de permitir a substituição de ResolveComponentForRenderMode (dotnet/aspnetcore
#51235).
Para obter mais informações, consulte Criar um aplicativo .NET MAUIBlazor Hybrid com um Blazor Web App.
Detetar o local de renderização, a interatividade e o modo de renderização atribuído em tempo de execução
Introduzimos uma nova API projetada para simplificar o processo de consulta de estados de componentes em tempo de execução. Esta API fornece os seguintes recursos:
- Determinar o local de execução atual do componente: Isso pode ser útil para depurar e otimizar o desempenho do componente.
- Verifique se o componente está sendo executado em um ambiente interativo: Isso pode ser útil para componentes que têm comportamentos diferentes com base na interatividade de seu ambiente.
- Recuperar o modo de renderização atribuído para o componente: Compreender o modo de renderização pode ajudar a otimizar o processo de renderização e melhorar o desempenho geral de um componente.
Para obter mais informações, consulte os modos de renderização do ASP.NET Core Blazor.
Experiência melhorada de reconexão do lado do servidor:
Os seguintes aprimoramentos foram feitos na experiência de reconexão padrão do lado do servidor:
Quando o usuário navega de volta para um aplicativo com um circuito desconectado, a reconexão é tentada imediatamente, em vez de esperar pela duração do próximo intervalo de reconexão. Isso melhora a experiência do usuário ao navegar até um aplicativo em uma guia do navegador que foi adormecida.
Quando uma tentativa de reconexão chega ao servidor, mas o servidor já liberou o circuito, uma atualização de página ocorre automaticamente. Isso evita que o usuário tenha que atualizar manualmente a página se isso provavelmente resultar em uma reconexão bem-sucedida.
O tempo de reconexão usa uma estratégia de backoff computada. Por padrão, as primeiras várias tentativas de reconexão ocorrem em rápida sucessão sem um intervalo de repetição antes que os atrasos computados sejam introduzidos entre as tentativas. Você pode personalizar o comportamento do intervalo de repetição especificando uma função para calcular o intervalo de repetição, como demonstra o exemplo de backoff exponencial a seguir:
Blazor.start({ circuit: { reconnectionOptions: { retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000 }, }, });
O estilo da interface do usuário de reconexão padrão foi modernizado.
Para obter mais informações, consulte ASP.NET Core BlazorSignalR guidance.
Serialização simplificada do estado de autenticação para Blazor Web Apps
Novas APIs facilitam a adição de autenticação a um Blazor Web Appexistente. Quando se cria um novo
Esses provedores transmitem o estado de autenticação do utilizador para o navegador. A autenticação no servidor em vez do cliente permite que o aplicativo acesse o estado de autenticação durante a pré-renderização e antes que o tempo de execução do .NET WebAssembly seja inicializado.
As implementações de
Isso funciona bem se você tiver começado a partir do modelo de projeto Blazor Web App e selecionado a opção Contas Individuais, mas é muito código para implementar ou copiar se estiver tentando adicionar autenticação a um projeto existente. Agora existem APIs, que agora fazem parte do modelo de projeto Blazor Web App, que podem ser chamadas nos projetos de servidor e cliente para adicionar essa funcionalidade:
- AddAuthenticationStateSerialization: Adiciona os serviços necessários para serializar o estado de autenticação no servidor.
- AddAuthenticationStateDeserialization: Adiciona os serviços necessários para desserializar o estado de autenticação no navegador.
Por padrão, a API serializa apenas o nome do lado do servidor e as declarações de função para acesso no navegador. Uma opção pode ser passada para AddAuthenticationStateSerialization para incluir todas as reivindicações.
Para mais informações, consulte as seguintes seções de autenticação e autorizaçãodo ASP.NET Core
Adicione páginas de SSR (renderização do lado do servidor) estáticas a um ambiente global interativo Blazor Web App
Com o lançamento do .NET 9, agora é mais simples adicionar páginas SSR estáticas a aplicativos que adotam interatividade global.
Essa abordagem só é útil quando o aplicativo tem páginas específicas que não podem funcionar com renderização interativa de servidor ou WebAssembly. Por exemplo, adote essa abordagem para páginas que dependem de leitura/gravação de cookies HTTP e só podem funcionar em um ciclo de solicitação/resposta em vez de renderização interativa. Para páginas que trabalham com renderização interativa, você não deve forçá-las a usar a renderização SSR estática, pois ela é menos eficiente e menos responsiva para o usuário final.
Marque qualquer página de componente Razor com o novo atributo [ExcludeFromInteractiveRouting]
atribuído com a diretiva @attribute
Razor:
@attribute [ExcludeFromInteractiveRouting]
A aplicação do atributo faz com que a navegação para a página saia do roteamento interativo. A navegação de entrada é forçada a realizar uma recarga de página inteira em vez de resolver a página por meio de roteamento interativo. A recarga de página inteira força o componente raiz de nível superior, normalmente o componente App
(App.razor
), a renderizar novamente a partir do servidor, permitindo que o aplicativo alterne para um modo de renderização de nível superior diferente.
O método de extensão RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting permite que o componente detete se o atributo [ExcludeFromInteractiveRouting]
é aplicado à página atual.
No componente App
, use o padrão no exemplo a seguir:
- As páginas que não são anotadas com o atributo
[ExcludeFromInteractiveRouting]
tornam-se automaticamente no modo de renderizaçãoInteractiveServer
com interatividade global. Você pode substituirInteractiveServer
porInteractiveWebAssembly
ouInteractiveAuto
para especificar um modo de renderização global padrão diferente. - As páginas anotadas com o atributo
[ExcludeFromInteractiveRouting]
adotam SSR estático (PageRenderMode
é atribuído anull
).
<!DOCTYPE html>
<html>
<head>
...
<HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
<Routes @rendermode="@PageRenderMode" />
...
</body>
</html>
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? PageRenderMode
=> HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}
Uma alternativa ao uso do método de extensão RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting é ler manualmente os metadados do endpoint usando HttpContext.GetEndpoint()?.Metadata
.
Esta funcionalidade é abordada pela documentação de referência em modos de renderização do ASP.NET Core Blazor.
Injeção por construtor
Razor componentes suportam a injeção de construtor.
No exemplo a seguir, a classe parcial de código subjacente injeta o serviço NavigationManager
usando um construtor primário :
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Para obter mais informações, consulte o ASP.NET Core Blazor injeção de dependência.
Compactação de Websocket para componentes do Servidor Interativo
Por padrão, os componentes do Servidor Interativo habilitam a compactação para conexões WebSocket
A compactação pode ser desativada definindo ConfigureWebSocketOptions
como null
, o que reduz a vulnerabilidade da aplicação a ataques, mas pode reduzir o desempenho.
.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)
Configure um CSP mais rigoroso de frame-ancestors
com um valor de 'none'
(aspas simples necessárias), que permite a compressão de WebSocket, mas impede que os navegadores integrem a aplicação em qualquer <iframe>
.
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Para obter mais informações, consulte os seguintes recursos:
- ASP.NET de orientação do Core BlazorSignalR
- Diretrizes de mitigação de ameaças para ASP.NET Core Blazor renderização interativa no lado do servidor
Manipular eventos de composição de teclado no Blazor
A nova propriedade KeyboardEventArgs.IsComposing
indica se o evento de teclado faz parte de uma sessão de composição. Acompanhar o estado de composição dos eventos do teclado é crucial para lidar com métodos internacionais de entrada de caracteres.
Adicionado o parâmetro OverscanCount
ao QuickGrid
O componente QuickGrid
agora expõe uma propriedade OverscanCount
que especifica quantas linhas adicionais são renderizadas antes e depois da região visível quando a virtualização está habilitada.
O OverscanCount
padrão é 3. O exemplo a seguir aumenta o OverscanCount
para 4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
O componente InputNumber
suporta o atributo type="range"
O componente InputNumber<TValue> agora suporta o atributo type="range"
, que cria uma entrada de intervalo compatível com a vinculação de modelo e a validação de formulário, sendo geralmente renderizada como um controlo deslizante ou um dial, em vez de uma caixa de texto:
<EditForm Model="Model" OnSubmit="Submit" FormName="EngineForm">
<div>
<label>
Nacelle Count (2-6):
<InputNumber @bind-Value="Model!.NacelleCount" max="6" min="2"
step="1" type="range" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private EngineSpecifications? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() {}
public class EngineSpecifications
{
[Required, Range(minimum: 2, maximum: 6)]
public int NacelleCount { get; set; }
}
}
Novos eventos de navegação melhorados
Acione retornos de chamada JavaScript antes ou depois da navegação aprimorada com novos ouvintes de eventos:
blazor.addEventListener("enhancednavigationstart", {CALLBACK})
blazor.addEventListener("enhancednavigationend", {CALLBACK})
Para obter mais informações, consulte ASP.NET Core Blazor JavaScript com renderização estática do lado do servidor (SSR estático).
SignalR
Esta seção descreve os novos recursos para SignalR.
Suporte a tipos polimórficos em SignalR Hubs
Os métodos de hub agora podem aceitar uma classe base em vez da classe derivada para habilitar cenários polimórficos. O tipo base precisa ser anotado com para permitir o polimorfismo.
public class MyHub : Hub
{
public void Method(JsonPerson person)
{
if (person is JsonPersonExtended)
{
}
else if (person is JsonPersonExtended2)
{
}
else
{
}
}
}
[JsonPolymorphic]
[JsonDerivedType(typeof(JsonPersonExtended), nameof(JsonPersonExtended))]
[JsonDerivedType(typeof(JsonPersonExtended2), nameof(JsonPersonExtended2))]
private class JsonPerson
{
public string Name { get; set; }
public Person Child { get; set; }
public Person Parent { get; set; }
}
private class JsonPersonExtended : JsonPerson
{
public int Age { get; set; }
}
private class JsonPersonExtended2 : JsonPerson
{
public string Location { get; set; }
}
Atividades melhoradas para SignalR
SignalR agora tem um ActivitySource para o servidor de hub e o cliente.
ActivitySource do servidor .NET SignalR
O SignalR ActivitySource chamado Microsoft.AspNetCore.SignalR.Server
emite eventos para chamadas de método de hub:
- Cada método é a sua própria atividade, por isso, qualquer coisa que emita uma atividade durante a chamada do método hub está incluída na atividade do método hub.
- As atividades do método Hub não têm um pai. Isso significa que eles não são agrupados na conexão de longa duração SignalR.
O exemplo a seguir utiliza o painel .NET Aspire e os pacotes de OpenTelemetry :
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
Adicione o seguinte código de inicialização ao arquivo Program.cs
:
using OpenTelemetry.Trace;
using SignalRChat.Hubs;
// Set OTEL_EXPORTER_OTLP_ENDPOINT environment variable depending on where your OTEL endpoint is.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
if (builder.Environment.IsDevelopment())
{
// View all traces only in development environment.
tracing.SetSampler(new AlwaysOnSampler());
}
tracing.AddAspNetCoreInstrumentation();
tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
});
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
var app = builder.Build();
Segue-se um exemplo de saída do Painel do Aspire :
Cliente .NET SignalR ActivitySource
O SignalR ActivitySource chamado Microsoft.AspNetCore.SignalR.Client
emite eventos para um cliente SignalR:
- O cliente .NET SignalR tem um
ActivitySource
chamadoMicrosoft.AspNetCore.SignalR.Client
. As invocações de hub agora criam uma extensão de cliente. Observe que outros clientes SignalR, como o cliente JavaScript, não suportam rastreamento. Este recurso será adicionado a mais clientes em versões futuras. - As invocações de hub no cliente e no servidor suportam propagação de contexto. A propagação do contexto de rastreamento permite o verdadeiro rastreamento distribuído. Agora é possível ver o fluxo de invocações do cliente para o servidor e vice-versa.
Veja como essas novas atividades aparecem no painel .NET Aspire:
SignalR suporta corte e AOT nativo
Continuando a jornada de AOT nativa de iniciada no .NET 8, habilitamos o corte e o suporte de compilação antecipada nativa (AOT) para cenários de cliente e servidor SignalR. Agora você pode aproveitar os benefícios de desempenho do uso do AOT nativo em aplicativos que usam SignalR para comunicações da Web em tempo real.
Primeiros passos
Instale o SDK mais recente do .NET 9.
Crie uma solução a partir do modelo webapiaot
em um shell de comando usando o seguinte comando:
dotnet new webapiaot -o SignalRChatAOTExample
Substitua o conteúdo do arquivo Program.cs
com o seguinte código SignalR:
using Microsoft.AspNetCore.SignalR;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddSignalR();
builder.Services.Configure<JsonHubProtocolOptions>(o =>
{
o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});
var app = builder.Build();
app.MapHub<ChatHub>("/chatHub");
app.MapGet("/", () => Results.Content("""
<!DOCTYPE html>
<html>
<head>
<title>SignalR Chat</title>
</head>
<body>
<input id="userInput" placeholder="Enter your name" />
<input id="messageInput" placeholder="Type a message" />
<button onclick="sendMessage()">Send</button>
<ul id="messages"></ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messages").appendChild(li);
});
async function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
await connection.invoke("SendMessage", user, message);
}
connection.start().catch(err => console.error(err));
</script>
</body>
</html>
""", "text/html"));
app.Run();
[JsonSerializable(typeof(string))]
internal partial class AppJsonSerializerContext : JsonSerializerContext { }
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
O exemplo anterior produz um executável nativo do Windows de 10 MB e um executável Linux de 10,9 MB.
Limitações
- Atualmente, apenas o protocolo JSON é suportado:
- Conforme mostrado no código anterior, os aplicativos que usam a serialização JSON e a AOT nativa devem usar o
System.Text.Json
Source Generator. - Isso segue a mesma abordagem das APIs mínimas.
- Conforme mostrado no código anterior, os aplicativos que usam a serialização JSON e a AOT nativa devem usar o
- No servidor SignalR, os parâmetros do método Hub do tipo
IAsyncEnumerable<T>
eChannelReader<T>
em queT
é um ValueType (struct
) não são suportados. O uso destes tipos resulta em uma exceção em tempo de execução na inicialização, tanto durante o desenvolvimento como no aplicativo publicado. Para obter mais informações, consulte SignalR: Usando IAsyncEnumerable<T> e ChannelReader<T> com ValueTypes em AOT nativo (dotnet/aspnetcore
#56179). -
Hubs fortemente tipados não são suportados com AOT nativo (
PublishAot
). O uso de hubs fortemente tipados com AOT nativo provocará avisos durante a compilação e publicação, além de uma exceção em tempo de execução. O uso de hubs fortemente tipados com corte (PublishedTrimmed
) é suportado. - Somente
Task
,Task<T>
,ValueTask
ouValueTask<T>
são suportados para tipos de retorno assíncronos.
APIs mínimas
Esta seção descreve os novos recursos para APIs mínimas.
Adicionadas InternalServerError
e InternalServerError<TValue>
ao TypedResults
A classe TypedResults é um veículo útil para retornar respostas baseadas em código de estado HTTP fortemente tipificadas de uma API mínima.
TypedResults
agora inclui métodos fábrica e tipos para retornar respostas "500 Internal Server Error" de endpoints. Aqui está um exemplo que retorna uma resposta 500:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Ligue ProducesProblem
e ProducesValidationProblem
em grupos de rotas
Os métodos de extensão ProducesProblem
e ProducesValidationProblem
foram atualizados para suportar seu uso em grupos de rotas. Esses métodos indicam que todos os endpoints em um grupo de rotas podem retornar respostas ProblemDetails
ou ValidationProblemDetails
para os fins dos metadados OpenAPI.
var app = WebApplication.Create();
var todos = app.MapGroup("/todos")
.ProducesProblem();
todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));
app.Run();
record Todo(int Id, string Title, boolean IsCompleted);
Problem
e ValidationProblem
tipos de resultado suportam a construção com valores IEnumerable<KeyValuePair<string, object?>>
Antes do .NET 9, a construção de de problemas e tipos de resultado ValidationProblem em APIs mínimas exigia que as propriedades errors
e extensions
fossem inicializadas com uma implementação de IDictionary<string, object?>
. Nesta versão, essas APIs de construção suportam sobrecargas que consomem IEnumerable<KeyValuePair<string, object?>>
.
var app = WebApplication.Create();
app.MapGet("/", () =>
{
var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
return TypedResults.Problem("This is an error with extensions",
extensions: extensions);
});
Obrigado ao usuário do GitHub joegoldman2 por esta contribuição!
OpenAPI
Esta seção descreve os novos recursos para OpenAPI
Suporte integrado para geração de documentos OpenAPI
O de especificação OpenAPI é um padrão para descrever APIs HTTP. O padrão permite que os desenvolvedores definam a forma das APIs que podem ser conectadas a geradores de clientes, geradores de servidor, ferramentas de teste, documentação e muito mais. No .NET 9, o ASP.NET Core fornece suporte interno para gerar documentos OpenAPI que representam APIs mínimas ou baseadas em controlador por meio do pacote Microsoft.AspNetCore.OpenApi
As seguintes chamadas de código realçadas:
-
AddOpenApi
registrar as dependências necessárias no contêiner DI do aplicativo. -
MapOpenApi
registrar os pontos de extremidade OpenAPI necessários nas rotas da aplicação.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/hello/{name}", (string name) => $"Hello {name}"!);
app.Run();
Instale o pacote Microsoft.AspNetCore.OpenApi
no projeto usando o seguinte comando:
dotnet add package Microsoft.AspNetCore.OpenApi
Execute o aplicativo e navegue até openapi/v1.json
para visualizar o documento OpenAPI gerado:
Os documentos OpenAPI também podem ser gerados em tempo de compilação adicionando o pacote Microsoft.Extensions.ApiDescription.Server
:
dotnet add package Microsoft.Extensions.ApiDescription.Server
Para modificar o local dos documentos OpenAPI emitidos, defina o caminho de destino na propriedade OpenApiDocumentsDirectory no arquivo de projeto do aplicativo:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
</PropertyGroup>
Execute dotnet build
e inspecione o arquivo JSON gerado no diretório do projeto.
A geração de documentos OpenAPI integrada do ASP.NET Core fornece suporte para várias personalizações e opções. Ele fornece transformadores de documento, operação e esquema e tem a capacidade de gerenciar vários documentos OpenAPI para o mesmo aplicativo.
Para saber mais sobre os novos recursos de documento OpenAPI do ASP.NET Core, consulte os novos documentos Microsoft.AspNetCore.OpenApi.
Microsoft.AspNetCore.OpenApi suporta redução e AOT nativo
OpenAPI no ASP.NET Core suporta trimming e AOT nativo. As etapas a seguir criam e publicam uma aplicação OpenAPI com otimização e AOT nativo:
Crie um novo projeto ASP.NET Core Web API (Native AOT).
dotnet new webapiaot
Adicione o pacote Microsoft.AspNetCore.OpenAPI.
dotnet add package Microsoft.AspNetCore.OpenApi
Atualize Program.cs
para permitir a geração de documentos OpenAPI.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Publique o aplicativo.
dotnet publish
Autenticação e autorização
Esta seção descreve os novos recursos para autenticação e autorização.
OpenIdConnectHandler adiciona suporte para solicitações de autorização por push (PAR)
Gostaríamos de agradecer ao Joe DeCock da Duende Software por adicionar Solicitações de Autorização Push (PAR) ao OpenIdConnectHandler do ASP.NET Core. Joe descreveu os antecedentes e a motivação para habilitar o PAR em sua proposta de API da seguinte forma:
Push Authorization Requests (PAR) é um padrão OAuth relativamente novo
que melhora a segurança dos fluxos OAuth e OIDC movendo os parâmetros de autorização do canal frontal para o canal traseiro. Ou seja, mover parâmetros de autorização das URLs de redirecionamento no navegador para permitir chamadas HTTP de máquina para máquina no back-end. Isto impede que um ciberatacante no navegador:
- Ver parâmetros de autorização, que podem vazar informação de identificação pessoal.
- Adulteração desses parâmetros. Por exemplo, o ciberatacante pode alterar o âmbito do acesso solicitado.
Empurrar os parâmetros de autorização também mantém as URLs de solicitação curtas. Os parâmetros de autorização podem ficar muito longos ao usar recursos OAuth e OIDC mais complexos, como Rich Authorization Requests. URLs que são longos causam problemas em muitos navegadores e infraestruturas de rede.
O uso do PAR é incentivado pelo grupo de trabalho FAPI dentro da OpenID Foundation. Por exemplo, o Perfil de Segurança FAPI2.0 requer o uso de PAR. Esse perfil de segurança é usado por muitos dos grupos que trabalham com open banking (principalmente na Europa), na saúde e em outros setores com altos requisitos de segurança.
O PAR é suportado por vários fornecedores de identity, incluindo
Para o .NET 9, decidimos habilitar o PAR por padrão se o documento de descoberta do provedor de identity anunciar suporte para PAR, uma vez que ele deve fornecer segurança aprimorada para provedores que o suportam. O documento de descoberta do provedor de identity geralmente é encontrado em .well-known/openid-configuration
. Se isso causar problemas, pode-se desativar o PAR por meio OpenIdConnectOptions.PushedAuthorizationBehavior da seguinte maneira:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("oidc", oidcOptions =>
{
// Other provider-specific configuration goes here.
// The default value is PushedAuthorizationBehavior.UseIfAvailable.
// 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
// and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
// of type 'OpenIdConnectOptions' could be found
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
});
Para garantir que a autenticação só seja bem-sucedida se o PAR for usado, use PushedAuthorizationBehavior.Require em vez disso. Essa alteração também introduz um novo evento OnPushAuthorization para OpenIdConnectEvents, que pode ser usado para personalizar a solicitação de autorização por push ou para manipulá-la manualmente. Consulte a proposta de API para obter mais detalhes.
Personalização de parâmetros OIDC e OAuth
Os manipuladores de autenticação OAuth e OIDC agora têm uma opção de AdditionalAuthorizationParameters
para facilitar a personalização dos parâmetros da mensagem de autorização que geralmente são incluídos como parte da cadeia de caracteres de consulta de redirecionamento. No .NET 8 e em versões anteriores, isso requer uma callback OnRedirectToIdentityProvider personalizada ou um método BuildChallengeUrl sobrescrito num manipulador personalizado. Aqui está um exemplo de código .NET 8:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("prompt", "login");
context.ProtocolMessage.SetParameter("audience", "https://api.example.com");
return Task.CompletedTask;
};
});
O exemplo anterior agora pode ser simplificado para o seguinte código:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Configurar sinalizadores de autenticação estendida HTTP.sys
Agora você pode configurar os sinalizadores HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
e HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL
HTTP.sys usando as novas propriedades EnableKerberosCredentialCaching
e CaptureCredentials
no HTTP.sys AuthenticationManager para otimizar como a autenticação do Windows é tratada. Por exemplo:
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
options.Authentication.EnableKerberosCredentialCaching = true;
options.Authentication.CaptureCredentials = true;
});
Diversos
As seções a seguir descrevem diversos novos recursos.
Nova biblioteca HybridCache
Importante
HybridCache
ainda está em pré-visualização, mas será totalmente lançado após .NET 9.0 numa futura versão menor do .NET Extensions.
A API HybridCache
preenche algumas lacunas nas APIs IDistributedCache e IMemoryCache existentes. Ele também adiciona novos recursos, tais como:
- Proteção "Stampede" para evitar o processamento paralelo do mesmo trabalho.
- Serialização configurável.
HybridCache
foi projetado para ser um substituto imediato para o uso de IDistributedCache
e IMemoryCache
existentes e fornece uma API simples para adicionar novo código de cache. Ele fornece uma API unificada para cache em processo e fora do processo.
Para ver como a API HybridCache
é simplificada, compare-a com o código que usa IDistributedCache
. Aqui está um exemplo de como é usar IDistributedCache
:
public class SomeService(IDistributedCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync
(string name, int id, CancellationToken token = default)
{
var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
var bytes = await cache.GetAsync(key, token); // Try to get from cache.
SomeInformation info;
if (bytes is null)
{
// Cache miss; get the data from the real source.
info = await SomeExpensiveOperationAsync(name, id, token);
// Serialize and cache it.
bytes = SomeSerializer.Serialize(info);
await cache.SetAsync(key, bytes, token);
}
else
{
// Cache hit; deserialize it.
info = SomeSerializer.Deserialize<SomeInformation>(bytes);
}
return info;
}
// This is the work we're trying to cache.
private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
CancellationToken token = default)
{ /* ... */ }
}
Dá muito trabalho acertar corretamente sempre, incluindo coisas como serialização. E no cenário de perda de cache, podes acabar com várias threads em simultâneo, todas a obter uma falha de cache, todas a buscar os dados subjacentes, todas a serializar esses dados e todas a enviá-los para o cache.
Para simplificar e melhorar este código com HybridCache
, primeiro precisamos adicionar a nova biblioteca Microsoft.Extensions.Caching.Hybrid
:
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Registre o serviço HybridCache
, como se registrasse uma IDistributedCache
implementação:
builder.Services.AddHybridCache(); // Not shown: optional configuration API.
Agora, a maioria da gestão de cache pode ser transferida para HybridCache
:
public class SomeService(HybridCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync
(string name, int id, CancellationToken token = default)
{
return await cache.GetOrCreateAsync(
$"someinfo:{name}:{id}", // Unique key for this combination.
async cancel => await SomeExpensiveOperationAsync(name, id, cancel),
token: token
);
}
}
Fornecemos uma implementação concreta da classe abstrata HybridCache
por meio da injeção de dependência, mas pretende-se que os desenvolvedores possam fornecer implementações personalizadas da API. A implementação HybridCache
lida com tudo relacionado ao cache, incluindo manipulação de operações simultâneas. O token cancel
aqui representa o cancelamento combinado de todos os chamadores simultâneos — não apenas o cancelamento do chamador que podemos ver (ou seja, token
).
Os cenários de alto rendimento podem ser ainda mais otimizados usando o padrão TState
, para evitar alguma sobrecarga de variáveis capturadas e callbacks por instância.
public class SomeService(HybridCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
{
return await cache.GetOrCreateAsync(
$"someinfo:{name}:{id}", // unique key for this combination
(name, id), // all of the state we need for the final call, if needed
static async (state, token) =>
await SomeExpensiveOperationAsync(state.name, state.id, token),
token: token
);
}
}
HybridCache
usa a implementação de IDistributedCache
configurada, se houver, para cache secundário fora do processo, por exemplo, usando Redis. Mas, mesmo sem um IDistributedCache
, o serviço HybridCache
ainda fornecerá armazenamento em cache no processo e proteção contra sobrecarga.
Uma nota sobre a reutilização de objetos
No código existente típico que usa IDistributedCache
, cada recuperação de um objeto do cache resulta em desserialização. Esse comportamento significa que cada chamador simultâneo obtém uma instância separada do objeto, que não pode interagir com outras instâncias. O resultado é a segurança do thread, pois não há risco de modificações simultâneas na mesma instância do objeto.
Como grande parte do uso de HybridCache
será adaptada do código IDistributedCache
existente, o HybridCache
preserva esse comportamento por defeito para evitar a introdução de erros de simultaneidade. No entanto, um determinado caso de uso é inerentemente thread-safe:
- Se os tipos que estão sendo armazenados em cache são imutáveis.
- Se o código não os modificar.
Para esses casos, informe HybridCache
que é seguro reutilizar instâncias:
- Marcando o tipo como
sealed
. A palavra-chavesealed
em C# significa que a classe não pode ser herdada. - Aplicando o atributo
[ImmutableObject(true)]
a ele. O atributo[ImmutableObject(true)]
indica que o estado do objeto não pode ser alterado após sua criação.
Ao reutilizar instâncias, HybridCache
pode reduzir a sobrecarga associada à utilização do CPU e à alocação de objetos na desserialização a cada chamada. Isso pode levar a melhorias de desempenho em cenários em que os objetos armazenados em cache são grandes ou acessados com frequência.
Outras características HybridCache
Como IDistributedCache
, HybridCache
suporta a remoção por chave com um método RemoveKeyAsync
.
HybridCache
também fornece APIs opcionais para implementações IDistributedCache
, para evitar alocações byte[]
. Esse recurso é implementado pelas versões de visualização dos pacotes Microsoft.Extensions.Caching.StackExchangeRedis
e Microsoft.Extensions.Caching.SqlServer
.
A serialização é configurada como parte do registo do serviço, com suporte para serializadores específicos de tipo e generalizados por meio dos métodos WithSerializer
e .WithSerializerFactory
, encadeados a partir da chamada AddHybridCache
. Por padrão, a biblioteca lida com string
e byte[]
internamente e usa System.Text.Json
para todo o resto, mas você pode usar protobuf, xml ou qualquer outra coisa.
HybridCache
suporta tempos de execução mais antigos do .NET, até .NET Framework 4.7.2 e .NET Standard 2.0.
Para obter mais informações sobre HybridCache
, consulte biblioteca HybridCache no ASP.NET Core
Melhorias na página de exceção do desenvolvedor
A página de exceção do desenvolvedor ASP.NET Core é exibida quando um aplicativo lança uma exceção não tratada durante o desenvolvimento. A página de exceção do desenvolvedor fornece informações detalhadas sobre a exceção e a solicitação.
A Preview 3 adicionou metadados de endpoints à página de exceção do desenvolvedor. O ASP.NET Core usa metadados de ponto de extremidade para controlar o comportamento do ponto de extremidade, como roteamento, cache de resposta, limitação de taxa, geração de OpenAPI e muito mais. A imagem a seguir mostra as novas informações de metadados na seção Routing
da página de exceção do desenvolvedor:
Ao testar a página de exceção do desenvolvedor, pequenas melhorias na qualidade de vida foram identificadas. Eles foram lançados na Prévia 4:
- Melhor quebra automática de texto. Cookies longos, valores de cadeia de caracteres de consulta e nomes de método não adicionam mais barras de rolagem horizontais do navegador.
- Texto maior que se encontra em projetos modernos.
- Tamanhos de tabela mais consistentes.
A imagem animada a seguir mostra a nova página de exceção do desenvolvedor:
Melhorias na depuração de dicionários
A visualização de depuração de dicionários e outras coleções de chave-valor apresenta um layout aprimorado. A chave é exibida na coluna de chave do depurador em vez de ser concatenada com o valor. As imagens a seguir mostram a exibição antiga e nova de um dicionário no depurador.
Antes:
Depois:
ASP.NET Core possui muitas coleções de chave-valor. Esta experiência de depuração melhorada aplica-se a:
- Cabeçalhos HTTP
- Parâmetros de consulta
- Formulários
- Biscoitos
- Ver dados
- Dados de rota
- Caraterísticas
Correção para códigos de erro 503 durante a reciclagem de aplicativos no IIS
Por padrão, agora há um atraso de 1 segundo entre o momento em que o IIS é notificado sobre uma reciclagem ou desligamento e quando o ANCM ordena que o servidor gerenciado comece o processo de desligamento. O atraso é configurável através da variável de ambiente ANCM_shutdownDelay
ou definindo a configuração do handler shutdownDelay
. Ambos os valores estão em milissegundos. O atraso é principalmente para reduzir a probabilidade de uma corrida onde:
- O IIS não começou a enfileirar solicitações para ir para o novo aplicativo.
- A ANCM começa a rejeitar novas solicitações que entram no aplicativo antigo.
Máquinas mais lentas ou com uso mais pesado da CPU podem querer ajustar esse valor para reduzir a probabilidade de 503.
Exemplo de configuração shutdownDelay
:
<aspNetCore processPath="dotnet" arguments="myapp.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
<handlerSettings>
<!-- Milliseconds to delay shutdown by.
this doesn't mean incoming requests will be delayed by this amount,
but the old app instance will start shutting down after this timeout occurs -->
<handlerSetting name="shutdownDelay" value="5000" />
</handlerSettings>
</aspNetCore>
A correção está no módulo ANCM instalado globalmente que vem do pacote de hospedagem.
ASP0026: Analisador para avisar quando [Autorizar] é substituído por [AllowAnonymous] de "mais longe"
Parece intuitivo que um atributo [Authorize]
colocado "mais perto" de uma ação MVC do que um atributo [AllowAnonymous]
substituiria o atributo [AllowAnonymous]
e forçaria a autorização. No entanto, não é necessariamente esse o caso. O que importa é a ordem relativa dos atributos.
O código a seguir mostra exemplos em que um atributo [Authorize]
mais próximo é substituído por um atributo [AllowAnonymous]
que está mais longe.
[AllowAnonymous]
public class MyController
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on the class
public IActionResult Private() => null;
}
[AllowAnonymous]
public class MyControllerAnon : ControllerBase
{
}
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public class MyControllerInherited : MyControllerAnon
{
}
public class MyControllerInherited2 : MyControllerAnon
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public IActionResult Private() => null;
}
[AllowAnonymous]
[Authorize] // Overridden by the preceding [AllowAnonymous]
public class MyControllerMultiple : ControllerBase
{
}
No .NET 9 Preview 6, introduzimos um analisador que destacará instâncias como essas em que um atributo [Authorize]
mais próximo é substituído por um atributo [AllowAnonymous]
que está mais longe de uma ação MVC. O aviso aponta para o atributo [Authorize]
substituído com a seguinte mensagem:
ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away
A ação correta a ser tomada se você vir esse aviso depende da intenção por trás dos atributos. O atributo [AllowAnonymous]
mais distante deve ser removido se estiver expondo involuntariamente o ponto de extremidade a usuários anônimos. Se o atributo [AllowAnonymous]
foi destinado a substituir um atributo [Authorize]
mais próximo, você pode repetir o atributo [AllowAnonymous]
após o atributo [Authorize]
para esclarecer a intenção.
[AllowAnonymous]
public class MyController
{
// This produces no warning because the second, "closer" [AllowAnonymous]
// clarifies that [Authorize] is intentionally overridden.
// Specifying AuthenticationSchemes can still be useful
// for endpoints that allow but don't require authenticated users.
[Authorize(AuthenticationSchemes = "Cookies")]
[AllowAnonymous]
public IActionResult Privacy() => null;
}
Métricas de conexão de Kestrel aprimoradas
Fizemos uma melhoria significativa nas métricas de conexão do Kestrelao incluir metadados sobre por que uma conexão falhou. A métrica kestrel.connection.duration
agora inclui o motivo de fechamento da conexão no atributo error.type
.
Aqui está uma pequena amostra dos error.type
valores:
-
tls_handshake_failed
- A conexão requer TLS e o handshake TLS falhou. -
connection_reset
- A conexão foi inesperadamente fechada pelo cliente enquanto as solicitações estavam em andamento. -
request_headers_timeout
- Kestrel fechou a conexão porque não recebeu cabeçalhos de solicitação a tempo. -
max_request_body_size_exceeded
- Kestrel fechou a conexão porque os dados carregados excederam o tamanho máximo.
Anteriormente, o diagnóstico de Kestrel problemas de conexão exigia que um servidor registrasse logs detalhados e de baixo nível. No entanto, os logs podem ser caros para gerar e armazenar, e pode ser difícil encontrar as informações certas entre os ruídos.
As métricas são uma alternativa muito mais barata que pode ser deixada em um ambiente de produção com impacto mínimo. As métricas coletadas podem gerar painéis e alertas. Uma vez que um problema é identificado em alto nível com métricas, uma investigação mais aprofundada usando registro em log e outras ferramentas pode começar.
Esperamos que métricas de conexão aprimoradas sejam úteis em muitos cenários:
- Investigando problemas de desempenho causados por tempos de vida de conexão curtos.
- Observando ataques externos constantes a Kestrel que afetam o desempenho e a estabilidade.
- Gravação de tentativas de ataques externos a Kestrel que o fortalecimento de segurança embutido em Kestrelimpediu.
Para obter mais informações, consulte ASP.NET Core métricas.
Personalizar endpoints de pipe nomeado Kestrel
O suporte de tubo nomeado do Kestrelfoi melhorado com opções de personalização avançadas. O novo método CreateNamedPipeServerStream
nas opções de tubo nomeado permite que os tubos sejam personalizados por cada ponto de extremidade.
Um exemplo de onde isso é útil é o aplicativo Kestrel que requer dois pontos de extremidade de canalização com diferentes níveis de segurança de acesso . A opção CreateNamedPipeServerStream
pode ser usada para criar pipes com configurações de segurança personalizadas, dependendo do nome do pipe.
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenNamedPipe("pipe1");
options.ListenNamedPipe("pipe2");
});
builder.WebHost.UseNamedPipes(options =>
{
options.CreateNamedPipeServerStream = (context) =>
{
var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName);
return NamedPipeServerStreamAcl.Create(context.NamedPipeEndPoint.PipeName, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity);
};
});
ExceptionHandlerMiddleware
opção para escolher o código de status com base no tipo de exceção
Uma nova opção ao configurar o ExceptionHandlerMiddleware
permite que os desenvolvedores de aplicativos escolham qual código de status retornar quando ocorrer uma exceção durante o processamento de solicitações. A nova opção altera o código de status que é definido na resposta ProblemDetails
do ExceptionHandlerMiddleware
.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Exclusão de métricas HTTP em determinados terminais e solicitações
O .NET 9 introduz a capacidade de desativar métricas HTTP para solicitações e pontos finais específicos. Optar por não registrar métricas é benéfico para endpoints frequentemente chamados por sistemas automatizados, como verificações de integridade. O registro de métricas para essas solicitações geralmente é desnecessário.
As solicitações HTTP para um endpoint podem ser excluídas das métricas ao adicionar metadados. Ou
- Adicione o atributo
[DisableHttpMetrics]
ao controlador de API Web, hub SignalR ou serviço gRPC. - Chame DisableHttpMetrics ao mapear endpoints no arranque da aplicação:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
A propriedade MetricsDisabled
foi adicionada ao IHttpMetricsTagsFeature
para:
- Cenários avançados em que uma solicitação não é mapeada para um endpoint.
- Desativando dinamicamente a coleta de métricas para solicitações HTTP específicas.
// Middleware that conditionally opts-out HTTP requests.
app.Use(async (context, next) =>
{
var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
if (metricsFeature != null &&
context.Request.Headers.ContainsKey("x-disable-metrics"))
{
metricsFeature.MetricsDisabled = true;
}
await next(context);
});
Suporte de proteção de dados para exclusão de chaves
Antes do .NET 9, as chaves de proteção de dados não eram excluíveis por design, para evitar a perda de dados. A exclusão de uma chave torna seus dados protegidos irrecuperáveis. Dado o seu pequeno tamanho, o acúmulo dessas chaves geralmente representou um impacto mínimo. No entanto, para acomodar serviços de execução extremamente longa, introduzimos a opção de eliminar chaves. Geralmente, apenas as chaves antigas devem ser excluídas. Só exclua chaves quando puder aceitar o risco de perda de dados em troca de economia de armazenamento. Recomendamos que as chaves de proteção de dados não sejam excluídas.
using Microsoft.AspNetCore.DataProtection.KeyManagement;
var services = new ServiceCollection();
services.AddDataProtection();
var serviceProvider = services.BuildServiceProvider();
var keyManager = serviceProvider.GetService<IKeyManager>();
if (keyManager is IDeletableKeyManager deletableKeyManager)
{
var utcNow = DateTimeOffset.UtcNow;
var yearAgo = utcNow.AddYears(-1);
if (!deletableKeyManager.DeleteKeys(key => key.ExpirationDate < yearAgo))
{
Console.WriteLine("Failed to delete keys.");
}
else
{
Console.WriteLine("Old keys deleted successfully.");
}
}
else
{
Console.WriteLine("Key manager does not support deletion.");
}
Middleware suporta Keyed DI
O middleware agora suporta DI com chave no construtor e no método Invoke
/InvokeAsync
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<MySingletonClass>("test");
builder.Services.AddKeyedScoped<MyScopedClass>("test2");
var app = builder.Build();
app.UseMiddleware<MyMiddleware>();
app.Run();
internal class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next,
[FromKeyedServices("test")] MySingletonClass service)
{
_next = next;
}
public Task Invoke(HttpContext context,
[FromKeyedServices("test2")]
MyScopedClass scopedService) => _next(context);
}
Confie no certificado de desenvolvimento HTTPS do ASP.NET Core no Linux
Em distribuições Linux baseadas em Ubuntu e Fedora, dotnet dev-certs https --trust
agora configura o certificado de desenvolvimento HTTPS do ASP.NET Core como um certificado confiável para:
- Navegadores Chromium, por exemplo, Google Chrome, Microsoft Edge e Chromium.
- Mozilla Firefox e navegadores derivados da Mozilla.
- APIs .NET, por exemplo, HttpClient
Anteriormente, --trust
funcionava apenas no Windows e macOS. A confiança do certificado é aplicada por usuário.
Para estabelecer confiança no OpenSSL, a ferramenta dev-certs
:
- Coloca o certificado em
~/.aspnet/dev-certs/trust
- Executa uma versão simplificada da ferramenta c_rehash do OpenSSL no diretório.
- Pede ao usuário para atualizar a variável de ambiente
SSL_CERT_DIR
.
Para estabelecer confiança no .NET, a ferramenta coloca o certificado no repositório de certificados My/Root
.
Para estabelecer confiança, se houver, nos bancos de dados NSS , a ferramenta procura perfis do Firefox no diretório home, ~/.pki/nssdb
e ~/snap/chromium/current/.pki/nssdb
. Para cada diretório encontrado, a ferramenta adiciona uma entrada ao nssdb
.
Modelos atualizados para as versões mais recentes do Bootstrap, jQuery e jQuery Validation
Os modelos e bibliotecas de projeto ASP.NET Core foram atualizados para usar as versões mais recentes do Bootstrap, jQuery e jQuery Validation, especificamente:
- Bootstrap 5.3.3
- jQuery 3.7.1
- Validação do jQuery 1.21.0