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.

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>

Experiência de reconexão aprimorada Blazor Server:

Os seguintes aprimoramentos foram feitos para a experiência de reconexão padrão Blazor Server:

  • A temporização de reconexão agora usa uma estratégia de retirada exponencial. As primeiras várias tentativas de reconexão ocorrem em sucessão rápida e, em seguida, um atraso é introduzido gradualmente entre as tentativas.

    Esse comportamento pode ser personalizado especificando uma função para calcular o intervalo de repetição. Por exemplo:

    Blazor.start({
      circuit: {
        reconnectionOptions: {
          retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000,
        },
      },
    });
    
  • Uma tentativa de reconexão é imediata quando o usuário navega de volta para um aplicativo com um circuito desconectado. Nesse caso, o intervalo de repetição automática é ignorado. Esse comportamento aprimora especialmente a experiência do usuário ao navegar até um aplicativo em uma guia do navegador que ficou em suspensão.

  • Se uma tentativa de reconexão atingir o servidor, mas a reconexão falhar porque o servidor já tiver liberado o circuito, uma atualização ocorrerá automaticamente. Uma atualização manual não será necessária se a reconexão bem-sucedida for provável.

  • O estilo da interface do usuário de reconexão padrão foi modernizado.

Modelo de solução híbrida e da Web do Blazor do .NET MAUI

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. Este modelo mostra como criar aplicativos cliente que podem ser direcionados para Android, iOS, Mac, Windows e Web, maximizando simultaneamente a reutilização de código.

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 Web Blazor e um aplicativo .NET MAUIBlazor Hybrid.
  • Os projetos criados são conectados para usar uma Biblioteca de Classes compartilhada Razor que contém todos os componentes e páginas da interface do usuário.
  • Código de exemplo que demonstra como usar a injeção de serviço para fornecer diferentes implementações de interface para o aplicativo Web Blazor Hybrid e Blazor. No .NET 8, esse é um processo manual documentado em Compilar um aplicativo .NET MAUIBlazor Hybrid com um aplicativo Web Blazor.

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

dotnet workload install maui

Em seguida, crie o modelo por meio da linha de comando, da seguinte maneira:

dotnet new maui-blazor-web

O modelo também está disponível no Visual Studio.

Observação

Atualmente, os aplicativos híbridos Blazor geram uma exceção se os modos de renderização Blazor são definidos no nível de página/componente. Para saber mais, confira n. 51235.

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

Novas APIs facilitam a adição de autenticação a um aplicativo Web Blazor existente. Quando você cria um projeto de aplicativo Web Blazor com autenticação usando Contas Individuais e habilita a interatividade baseada em WebAssembly, o projeto inclui um AuthenticationStateProvider personalizado nos projetos do servidor e também 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 do WebAssembly seja inicializado.

As implementações do AuthenticationStateProvider personalizado usam o serviço PersistentComponentState para serializar o estado de autenticação em comentários HTML e, em seguida, lê-lo de volta do WebAssembly para criar uma nova instância de AuthenticationState.

Isso funcionará bem se você tiver iniciado a partir do modelo de projeto do aplicativo Web Blazor e selecionado a opção Contas Individuais, mas é muito código para implementar por conta própria ou para copiar, se você está tentando adicionar autenticação a um projeto existente.

Agora há APIs que podem ser chamadas nos projetos de servidor e de cliente para adicionar essa funcionalidade:

  • No projeto do servidor, use AddAuthenticationStateSerialization em Program.cs para adicionar os serviços necessários para serializar o estado de autenticação no servidor.

    builder.Services.AddRazorComponents()
        .AddInteractiveWebAssemblyComponents()
        .AddAuthenticationStateSerialization();
    
  • No projeto do cliente, use AddAuthenticationStateDeserialization em Program.cs para adicionar os serviços necessários para desserializar o estado de autenticação no navegador.

    builder.Services.AddAuthorizationCore();
    builder.Services.AddCascadingAuthenticationState();
    builder.Services.AddAuthenticationStateDeserialization();
    

Por padrão, essas APIs só serializarão o nome do lado do servidor e as declarações de função para acesso no navegador. Para incluir todas as declarações, use AuthenticationStateSerializationOptions no servidor:

builder.Services.AddRazorComponents()
    .AddInteractiveWebAssemblyComponents()
    .AddAuthenticationStateSerialization(options => options.SerializeAllClaims = true);

O modelo de projeto do Aplicativo Web Blazor foi atualizado para usar essas APIs.

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.

Detectar o modo de renderização do componente atual em runtime

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

  • Determinar o ambiente de execução atual do componente: este recurso permite identificar o ambiente no qual o componente está em execução no momento. Pode ser particularmente útil para depurar e otimizar o desempenho do componente.
  • Verificar se o componente está em execução em um ambiente interativo: esta funcionalidade permite verificar se o componente está operando 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: este recurso permite que você obtenha 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 melhorar o desempenho geral do componente.

ComponentBase (e por extensão seus componentes), ofereça uma nova propriedade Platform (que deverá em breve ser renomeada para RendererInfo) que expõe as propriedades Name, IsInteractive e AssignedRenderMode:

  • Platform.Name: onde o componente está em execução: Static, Server, WebAssembly ou WebView.
  • Platform.IsInteractive: indica se a plataforma dá suporte à interatividade. Isso é true para todas as implementações, exceto Static.
  • AssignedRenderMode: expõe o valor do modo de renderização definido na hierarquia de componentes, se houver, por meio do render-mode atributo em um componente raiz ou o atributo [RenderMode]. Os valores podem ser InteractiveServer, InteractiveAuto ou InteractiveWebassembly.

Esses valores são mais úteis durante a pré-geração, pois mostram para onde o componente fará a transição após a pré-geração. Saber para onde o componente fará a transição após a pré-renderização geralmente é útil para renderizar conteúdo diferente. Por exemplo, considere criar um componente Form renderizado interativamente. Você pode optar por desabilitar as entradas durante a pré-renderização. Depois que o componente se torna interativo, as entradas são habilitadas.

Como alternativa, se o componente não for renderizado em um contexto interativo, considere a marcação de renderização para dar suporte à execução de qualquer ação por meio da mecânica da Web regular.

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%