Compartilhar via


Novidades do ASP.NET Core 9.0

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

Este artigo foi atualizado para o .NET 9 Preview 5.

Blazor

Esta seção descreve os novos recursos do Blazor.

.NET MAUIBlazor Hybrid e um modelo de solução de Aplicativo Web

Um novo modelo de solução facilita a criação de aplicativos de cliente Web do Blazor e nativos do .NET MAUI que compartilham a mesma interface do usuário. Esse modelo mostra como criar aplicativos cliente que maximizam a reutilização de código e opções para 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 aplicativo Blazor para a web (renderização automática interativa global) e um aplicativo .NET MAUIBlazor Hybrid.
  • Os projetos criados usam uma biblioteca de classes do Razor (RCL) compartilhada para manter os componentes Razor da interface do usuário.
  • Está incluída uma amostra de código que demonstra como usar a injeção de dependências para fornecer diferentes implementações de interface para o aplicativo Blazor Hybrid e o Aplicativo Web do Blazor.

Para começar, instale o SDK do .NET 9 e instale a carga de trabalho do .NET MAUI que contém o modelo:

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, ocorrerá uma exceção se os modos de renderização do Blazor forem definidos no nível por página/componente. Para obter mais informações, confira BlazorO WebView precisa de uma maneira de habilitar a substituição de ResolveComponentForRenderMode (dotnet/aspnetcore nº 51235).

Para obter mais informações, confira Criar um aplicativo .NET MAUIBlazor Hybrid com um aplicativo web do Blazor.

Otimização de entrega de ativos estáticos

MapStaticAssets é um novo middleware que ajuda a otimizar a entrega de ativos estáticos em qualquer aplicativo do ASP.NET Core, incluindo aplicativos Blazor.

Para obter mais informações, consulte um dos seguintes recursos:

Detectar o local de renderização, a interatividade e o modo de renderização atribuído no runtime

Introduzimos uma nova API projetada para simplificar o processo de consulta de estados de componentes no runtime. Essa API fornece os seguintes recursos:

  • Determinar o local de execução atual do componente, o que pode ser particularmente útil para depurar e otimizar o desempenho do componente.
  • Verificar se o componente está em execução em um ambiente interativo, o 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 ao componente: entender o modo de renderização pode ajudar a otimizar o processo de renderização e aprimorar o desempenho de um componente de modo geral.

Experiência de reconexão aprimorada 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 aguardar todo o intervalo de tempo até a próxima reconexão. Isso aprimora a experiência do usuário quando este navega até um aplicativo em uma guia do navegador que entrou no modo de suspensão.

  • Quando uma tentativa de reconexão chega ao servidor, mas o servidor já liberou o circuito, a página é atualizada automaticamente. Isso impede que o usuário precise atualizar a página manualmente se o provável resultado for uma reconexão bem-sucedida.

  • O tempo decorrido até a reconexão usa uma estratégia de espera calculada. 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 calculados 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 espera 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, confira as diretrizes BlazorSignalRdo ASP.NET Core.

Serialização de estado de autenticação simplificada para Aplicativos Web Blazor

Novas APIs facilitam o acréscimo de autenticação a um Aplicativo Web do Blazor existente. Quando você cria um novo Aplicativo Web do Blazor com autenticação usando Contas Individuais e habilita a interatividade baseada em WebAssembly, o projeto inclui um AuthenticationStateProvider personalizado tanto nos projetos do servidor quanto do cliente.

Esses provedores fazem com que o estado de autenticação do usuário flua para o navegador. A autenticação no servidor em vez de no cliente permite que o aplicativo acesse o estado de autenticação durante a pré-renderização e antes que o runtime Blazor WebAssembly seja inicializado.

As implementações de AuthenticationStateProvider personalizadas usam o Serviço do Estado de Componente Persistente (PersistentComponentState) para serializar o estado de autenticação em comentários HTML e, em seguida, lê-lo de volta a partir do WebAssembly para criar uma nova instância de AuthenticationState.

Isso funcionará bem se você tiver começado a partir do modelo de projeto do Aplicativo Web do Blazor e selecionado a opção Contas Individuais, mas envolve muito código para implementar por conta própria ou para copiar, se você estiver tentando adicionar autenticação a um projeto existente. Agora temos APIs, que já fazem parte do modelo de projeto do Aplicativo Web do Blazor e podem ser chamadas nos projetos do servidor e do 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 só serializa as declarações de nome e função do lado do servidor para acesso no navegador. Uma opção pode ser repassada para AddAuthenticationStateSerialization de modo a incluir todas as declarações.

Para obter mais informações, confira as seguintes seções do artigo **:

Adicionar páginas de SSR (renderização do lado do servidor) estático a um aplicativo Web Blazor globalmente interativo

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 a renderização interativa do Servidor ou WebAssembly. Por exemplo, adote essa abordagem para páginas que dependem da leitura/gravação de HTTP cookiee só podem funcionar em um ciclo de solicitação/resposta em vez de renderização interativa. Para páginas que funcionam 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 @attributeRazor:

@attribute [ExcludeFromInteractiveRouting]

A aplicação do atributo faz com que a navegação na página saia do roteamento interativo. A navegação de entrada é forçada a executar um recarregamento de página inteira, resolvendo a página por meio de roteamento interativo. O recarregamento de página inteira força o componente raiz de nível superior, normalmente o componente App (App.razor), a gerar novamente do servidor, permitindo que o aplicativo mude para um modo de renderização de nível superior diferente.

O método de extensão HttpContext.AcceptsInteractiveRouting permite que o componente detecte se [ExcludeFromInteractiveRouting] é aplicado à página atual.

No componente App, use o padrão no exemplo a seguir:

  • Páginas que não são anotadas com [ExcludeFromInteractiveRouting] padrão para o modo de renderização InteractiveServer com interatividade global. Você pode substituir InteractiveServer por InteractiveWebAssembly ou InteractiveAuto para especificar um modo de renderização global padrão diferente.
  • Páginas anotadas com [ExcludeFromInteractiveRouting] adotar SSR estático (PageRenderMode é atribuído null).
<!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 HttpContext.AcceptsInteractiveRouting é ler metadados de ponto de extremidade manualmente usando HttpContext.GetEndpoint()?.Metadata.

Esse recurso é abordado pela documentação de referência nos Modos de renderização Blazor do ASP.NET Core .

Injeção de construção

Os componentes Razor dão suporte à injeção do construtor.

No exemplo a seguir, a classe parcial (code-behind) injeta o serviço NavigationManager usando um construtor primário:

public partial class ConstructorInjection(NavigationManager navigation)
{
    protected NavigationManager Navigation { get; } = navigation;
}

Para saber mais, confira Injeção de dependência do Blazor no ASP.NET Core.

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 e definem uma diretiva frame-ancestorsPolítica de Segurança de Conteúdo (CSP) definida como 'self', que só permite a incorporação do aplicativo em uma origem <iframe> da qual o aplicativo é servido quando a compactação está habilitada ou quando uma configuração para o contexto do WebSocket é fornecida.

A compactação pode ser desabilitada definindo ConfigureWebSocketOptions como null, o que reduz a vulnerabilidade do aplicativo a ataques, mas pode resultar em desempenho reduzido:

.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)

Configure um CSP frame-ancestors mais restrito com um valor de 'none' (aspas simples obrigatórias), que permite a compactação do WebSocket mas impede que os navegadores incorporem o aplicativo em qualquer <iframe>:

.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")

Para saber mais, consulte os recursos a seguir:

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 de eventos de teclado é crucial para lidar com métodos de entrada de caracteres internacionais.

Parâmetro OverscanCount adicionado a 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>

SignalR

Esta seção descreve os novos recursos do SignalR.

Suporte a tipos polimórficos em Hubs SignalR

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

APIs mínimas

Essa seção descreve novos recursos para APIs mínimas.

InternalServerError e InternalServerError<TValue> foram adicionados a TypedResults

A classe TypedResults é um veículo útil para retornar respostas baseadas em código de status HTTP fortemente tipado de uma API mínima. TypedResults agora inclui métodos e tipos de fábrica para retornar respostas de "500 Erro Interno do Servidor" de pontos de extremidade. Este é um exemplo que retorna uma resposta 500:

var app = WebApplication.Create();

app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));

app.Run();

OpenAPI

Suporte interno para geração de documentos OpenAPI

A especificação OpenAPI é um padrão para descrever APIs HTTP. O padrão permite que os desenvolvedores definam a forma de APIs que podem ser conectadas a geradores de cliente, geradores de servidor, ferramentas de teste, documentação e muito mais. No .NET 9 Preview, o ASP.NET Core fornece suporte interno para gerar documentos OpenAPI que representam APIs baseadas em controlador ou mínimas por meio do pacote Microsoft.AspNetCore.OpenApi.

As seguintes chamadas de código realçadas:

  • AddOpenApi para registrar as dependências necessárias no contêiner de DI do aplicativo.
  • MapOpenApi para registrar os pontos de extremidade OpenAPI necessários nas rotas do aplicativo.
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 --prerelease

Execute o aplicativo e navegue até openapi/v1.json para exibir o documento OpenAPI gerado:

Documento do OpenAPI

Documentos OpenAPI também podem ser gerados no tempo de build adicionando o pacote Microsoft.Extensions.ApiDescription.Server:

dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease

No arquivo de projeto do aplicativo, adicione o seguinte:

<PropertyGroup>
  <OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
  <OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
</PropertyGroup>

Execute dotnet build e inspecione o arquivo JSON gerado no diretório do projeto.

Geração de documentos OpenAPI em tempo de build

A geração de documentos OpenAPI interna do ASP.NET Core oferece suporte para várias personalizações e opções. Ela fornece transformadores de documento e operação 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.

Autenticação e autorização

Esta seção descreve os novos recursos de autenticação e autorização.

Personalização do parâmetro OIDC e OAuth

Os manipuladores de autenticação OAuth e OIDC agora têm uma opção AdditionalAuthorizationParameters para facilitar a personalização dos parâmetros de mensagem de autorização que geralmente são incluídos como parte da cadeia de caracteres de consulta de redirecionamento. No .NET 8 e anterior, isso requer um retorno de chamada personalizado OnRedirectToIdentityProvider ou um método substituído BuildChallengeUrl em um 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.sys HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING e HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL usando as novas propriedades EnableKerberosCredentialCaching e CaptureCredentials no HTTP.sys AuthenticationManager para otimizar o modo 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 de HybridCache

A API HybridCache abre algumas lacunas nas APIs IDistributedCache e IMemoryCache existentes. Ele também adiciona novos recursos, como:

  • Proteção "Stampede" para impedir buscas paralelas do mesmo trabalho.
  • Serialização configurável.

HybridCache foi projetado para ser uma substituição suspensa para uso de IDistributedCache e IMemoryCache existentes e fornece uma API simples para adicionar um 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)
    { /* ... */ }
}

Isso é muito trabalho para acertar a cada vez, incluindo coisas como serialização. E no cenário de falha de cache, você pode acabar com vários threads simultâneos, todos recebendo uma falha de cache, todos buscando os dados subjacentes, todos serializando-os e todos enviando esses dados para o cache.

Para simplificar e melhorar esse 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 você registraria uma implementação de IDistributedCache:

services.AddHybridCache(); // Not shown: optional configuration API.

Agora, a maioria das preocupações de cache pode ser descarregada 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 a intenção é que os desenvolvedores possam fornecer implementações personalizadas da API. A implementação HybridCache lida com tudo relacionado ao cache, incluindo o tratamento 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).

Cenários de alta taxa de transferência podem ser otimizados ainda mais usando o padrão TState, para evitar alguma sobrecarga de variáveis capturadas e retornos de chamada 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 o cache fora de processo secundário, por exemplo, usando o Redis. Mas mesmo sem um IDistributedCache, o serviço HybridCache ainda fornecerá proteção contra cache e "debandada" em processo.

Uma anotação sobre a reutilização do objeto

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 de thread, pois não há risco de modificações simultâneas na mesma instância de objeto.

Como muitos HybridCache uso serão adaptados do código IDistributedCache existente, HybridCache preserva esse comportamento por padrão para evitar a introdução de bugs de simultaneidade. No entanto, um determinado caso de uso é inerentemente thread-safe:

  • Se os tipos que estão sendo armazenados em cache forem imutáveis.
  • Se o código não os modificar.

Nesses casos, informe HybridCache que é seguro reutilizar instâncias:

  • Marcando o tipo como sealed. A palavra-chave sealed 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 depois de criado.

Ao reutilizar instâncias, HybridCache pode reduzir a sobrecarga de alocações de CPU e objeto associadas à desserialização por 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.

Outros recursos de HybridCache

Assim como IDistributedCache, HybridCache dá suporte à remoção por chave com um método RemoveKeyAsync.

HybridCache também fornece APIs opcionais para implementações de IDistributedCache, para evitar alocações byte[]. Esse recurso é implementado pelas versões prévias dos pacotes Microsoft.Extensions.Caching.StackExchangeRedis e Microsoft.Extensions.Caching.SqlServer.

A serialização é configurada como parte do registro do serviço, com suporte para serializadores específicos do tipo e generalizados por meio dos métodos WithSerializer e .WithSerializerFactory, encadeados da chamada AddHybridCache. Por padrão, a biblioteca manipula string e byte[] internamente e usa System.Text.Json para todo o resto, mas você pode usar protobuf, xml ou qualquer outra coisa.

HybridCache dá suporte a runtimes 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 do 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 versão prévia 3 adicionou metadados de ponto de extremidade à página de exceção do desenvolvedor. 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 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:

As novas informações de metadados na página de exceção do desenvolvedor

Durante o teste da página de exceção do desenvolvedor, foram identificados pequenos aprimoramentos de qualidade de vida. Eles foram enviados na versão prévia 4:

  • Melhor disposição de texto. Longos cookies, de cadeia de caracteres de consulta e de método não adicionam mais barras de rolagem horizontal do navegador.
  • Texto maior que é encontrado em designs modernos.
  • Tamanhos de tabela mais consistentes.

A imagem animada a seguir mostra a nova página de exceção do desenvolvedor:

A nova página de exceção do desenvolvedor

Melhorias de depuração do dicionário

A exibição de depuração de dicionários e outras coleções de chave-valor tem 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:

A experiência anterior do depurador

Depois:

A nova experiência do depurador

O ASP.NET Core tem muitas coleções de chave-valor. Essa experiência de depuração aprimorada se aplica a:

  • Cabeçalhos HTTP
  • Cadeias de consulta
  • Formulários
  • Cookies
  • Exibir dados
  • Rotear dados
  • Recursos

Correção para 503 durante o ciclo de aplicativo no IIS

Por padrão, agora há um atraso de 1 segundo entre quando o IIS é notificado de um ciclo ou desligamento e quando o ANCM informa ao servidor gerenciado para começar a desligar. O atraso é configurável por meio da variável de ambiente ANCM_shutdownDelay ou definindo a configuração do manipulador de shutdownDelay. Ambos os valores estão em milissegundos. O atraso é principalmente para reduzir a probabilidade de uma corrida em que:

  • O IIS não começou a enfileirar solicitações para ir para o novo aplicativo.
  • O ANCM começa a rejeitar novas solicitações que entram no aplicativo antigo.

Computadores ou computadores mais lentos 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 globalmente instalado que vem do pacote de hospedagem.

Otimizando a entrega de ativos da Web estáticos

A criação de aplicativos Web com desempenho inclui a otimização da entrega de ativos para o navegador. Isso envolve muitos aspectos, como:

MapStaticAssets é um novo middleware que ajuda a otimizar 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 (drop-in) para UseStaticFiles.

MapStaticAssets opera combinando processos de build e de 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 runtime para servir esses arquivos ao navegador com eficiência.

MapStaticAssets pode substituir UseStaticFiles na maioria das situações; no entanto, ele é otimizado para atender aos ativos dos quais o aplicativo tem conhecimento no tempo de compilação e publicação. Se o aplicativo atender ativos de outros locais, como disco ou recursos inseridos, o UseStaticFiles deverá ser usado.

MapStaticAssets fornece os seguintes benefícios não encontrados com UseStaticFiles:

  • Compactação de tempo de build para todos os ativos no aplicativo:
    • gzip durante o desenvolvimento e gzip + brotli durante a publicação.
    • Todos os ativos são compactados com a meta de reduzir o tamanho dos ativos ao mínimo.
  • ETags baseado em conteúdo: as Etags para cada recurso são a cadeia de caracteres codificada em Base64 do hash SHA-256 do conteúdo. Isso garante que o navegador só recarregue um arquivo se o conteúdo tiver sido alterado.

A tabela a seguir mostra os tamanhos originais e compactados do CSS e dos arquivos JS no modelo padrão do Razor Pages:

Arquivo Original Compressed Redução de %
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 Blazor da interface do usuário do Fluent:

Arquivo Original Compressed Redução de %
fluent.js 384 73 80.99%
fluent.css 94 11 88,30%
Total 478 84 82,43%

Para um total de 478 KB descompactados a 84 KB compactados.

A tabela a seguir mostra os tamanhos originais e compactados usando a biblioteca de componentes MudBlazorBlazor:

Arquivo Original Compressed 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 ocorre automaticamente ao usar MapStaticAssets. Quando uma biblioteca é adicionada ou atualizada, por exemplo, com o novo JavaScript ou CSS, os ativos são otimizados como parte do build. 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.

Habilitar a compactação dinâmica no servidor versus usar MapStaticAssets

MapStaticAssets tem as seguintes vantagens em relação à compactação dinâmica no servidor:

  • É mais simples porque não há nenhuma configuração específica do servidor.
  • É mais eficaz porque os ativos são compactados no momento da compilação.
  • Permite que o desenvolvedor gaste tempo extra durante o processo de build para garantir que os ativos sejam do tamanho mínimo.

Considere a tabela a seguir comparando a compactação MudBlazor com a compactação dinâmica do IIS e MapStaticAssets:

Gzip do IIS MapStaticAssets Redução de MapStaticAssets
≅ 90 37,5 59%