Compartir a través de


Novedades de ASP.NET Core 9.0

En este artículo se resaltan los cambios más importantes de ASP.NET Core 9.0, con vínculos a la documentación pertinente.

Este artículo se ha actualizado para .NET 9 Preview 5.

Blazor

En esta sección se describen las nuevas características de Blazor.

.NET MAUIBlazor Hybrid y plantilla de solución de aplicación web

Una nueva plantilla de solución facilita la creación de aplicaciones .NET MAUI nativas y Blazor cliente web que comparten la misma interfaz de usuario. Esta plantilla muestra cómo crear aplicaciones cliente que maximicen la reutilización del código y tengan como destino Android, iOS, Mac, Windows y la Web.

Entre las características clave de esta plantilla se incluyen las siguientes:

  • La capacidad de elegir un modo de representación interactivo de Blazor para la aplicación web.
  • Creación automática de los proyectos adecuados, incluyendo una aplicación web de Blazor (representación automática interactiva global) y una aplicación de .NET MAUIBlazor Hybrid.
  • Los proyectos creados usan una biblioteca de clases compartidas (RCL) Razor para mantener los componentes de Razor de la interfaz de usuario.
  • Se incluye código de ejemplo que demuestra cómo usar la inyección de dependencias para proporcionar diferentes implementaciones de interfaces para la aplicación de Blazor Hybrid y la aplicación web de Blazor.

Para empezar, instale el SDK de .NET 9 y la carga de trabajo .NET MAUI, que contiene la plantilla:

dotnet workload install maui

Cree una solución a partir de la plantilla de proyecto en un shell de comandos mediante el comando siguiente:

dotnet new maui-blazor-web

La plantilla también está disponible en Visual Studio.

Nota:

Actualmente, se produce una excepción si se definen modos de representación de Blazor a nivel de página/componente. Para obtener más información, vea BlazorWebView necesita una manera de habilitar la invalidación de ResolveComponentForRenderMode (dotnet/aspnetcore #51235).

Para más información, consulte Compilar una aplicación de .NET MAUIBlazor Hybrid con una aplicación web de Blazor.

Optimización de entrega de recursos estáticos

MapStaticAssets es un nuevo middleware que ayuda a optimizar la entrega de recursos estáticos en cualquier aplicación ASP.NET Core, incluidas las aplicaciones de Blazor.

Para más información, consulte cualquiera de los siguientes recursos:

Detección de la ubicación de representación, interactividad y modo de representación asignado en tiempo de ejecución

Hemos introducido una nueva API diseñada para simplificar el proceso de consulta de estados de componentes en tiempo de ejecución. Esta API proporciona las siguientes funcionalidades:

  • Determinar la ubicación de ejecución actual del componente: esto puede ser especialmente útil para depurar y optimizar el rendimiento de los componentes.
  • Comprobar si el componente se ejecuta en un entorno interactivo: esto puede resultar útil para los componentes que tienen comportamientos diferentes en función de la interactividad de su entorno.
  • Recuperar el modo de representación asignado para el componente: comprender el modo de representación puede ayudar a optimizar el proceso de representación y mejorar el rendimiento general de un componente.

Experiencia mejorada de reconexión del lado servidor:

Se han realizado las siguientes mejoras en la experiencia de reconexión predeterminada del lado servidor:

  • Cuando el usuario vuelve a una aplicación con un circuito desconectado, se intenta volver a conectar inmediatamente en lugar de esperar la duración del siguiente intervalo de reconexión. Esto mejora la experiencia del usuario cuando navega a una aplicación en una pestaña del explorador que ha pasado a estado de suspensión.

  • Cuando un intento de reconexión llega al servidor, pero el servidor ya ha liberado el circuito, se produce automáticamente una actualización de página. Esto evita que el usuario tenga que actualizar manualmente la página si es probable que se produzca una reconexión correcta.

  • El tiempo de reconexión usa una estrategia de retroceso calculada. De forma predeterminada, los primeros intentos de reconexión se producen en sucesión rápida sin un intervalo de reintento antes de que se introduzcan retrasos calculados entre intentos. Puede personalizar el comportamiento del intervalo de reintento especificando una función para calcular el intervalo de reintento, como se muestra en el ejemplo de retroceso exponencial siguiente:

    Blazor.start({
      circuit: {
        reconnectionOptions: {
          retryIntervalMilliseconds: (previousAttempts, maxRetries) => 
            previousAttempts >= maxRetries ? null : previousAttempts * 1000
        },
      },
    });
    
  • Se ha modernizado el estilo de la interfaz de usuario de reconexión predeterminada.

Para obtener más información, vea las Instrucciones de ASP.NET Core BlazorSignalR.

Serialización simplificada del estado de autenticación para aplicaciones web Blazor

Las nuevas API facilitan la adición de autenticación a una aplicación web de Blazor existente. Al crear una aplicación web de Blazor con autenticación mediante cuentas individuales y habilitar la interactividad basada en WebAssembly, el proyecto incluye un elemento AuthenticationStateProvider personalizado en los proyectos de servidor y cliente.

Estos proveedores hacen que el estado de autenticación del usuario fluya al explorador. La autenticación en el servidor en lugar del cliente permite que la aplicación acceda al estado de autenticación durante la representación previa y antes de que se inicialice el runtime de Blazor WebAssembly.

Las implementaciones personalizadas de AuthenticationStateProvider usan el servicio de estado de componente persistente (PersistentComponentState) para serializar el estado de autenticación en comentarios HTML y, después, volverlo a leer desde WebAssembly para crear una instancia de AuthenticationState.

Esto funciona bien si ha empezado desde la plantilla de proyecto de aplicación web de Blazor y ha seleccionado la opción Cuentas individuales, pero es mucho código que implementar personalmente o para copiarlo si lo que intenta es agregar autenticación a un proyecto existente. Ahora existen API, que forman parte de la plantilla de proyecto de aplicación web Blazor, que pueden llamarse en los proyectos del servidor y del cliente para agregar esta funcionalidad:

  • AddAuthenticationStateSerialization: agrega los servicios necesarios para serializar el estado de autenticación en el servidor.
  • AddAuthenticationStateDeserialization: agrega los servicios necesarios para deserializar el estado de autenticación en el explorador.

De manera predeterminada, estas API solo serializan las notificaciones de rol y nombre del lado servidor para el acceso en el explorador. Se puede pasar una opción a AddAuthenticationStateSerialization para incluir todas las notificaciones.

Para más información, consulte las secciones siguientes del artículo **:

Agregar páginas estáticas de representación del lado del servidor (SSR) a una aplicación web Blazor interactiva de manera global

Con el lanzamiento de .NET 9, ahora es más sencillo agregar páginas de SSR estática a las aplicaciones que adoptan interactividad global.

Este enfoque solo es útil cuando la aplicación tiene páginas específicas que no pueden trabajar con la representación interactiva de Servidor o WebAssembly. Por ejemplo, adopte este enfoque para las páginas que dependen de la lectura y escritura de cookies HTTP y solo pueden funcionar en un ciclo de solicitud/respuesta en lugar de la representación interactiva. En el caso de las páginas que funcionan con la representación interactiva, no debe obligarlas a usar la representación SSR estática, ya que es menos eficaz y tiene menos capacidad de respuesta para el usuario final.

Marque cualquier página de componentes Razor con el nuevo atributo [ExcludeFromInteractiveRouting] asignado con la directiva Razor@attribute:

@attribute [ExcludeFromInteractiveRouting]

La aplicación del atributo hace que la navegación a la página salga del enrutamiento interactivo. La navegación entrante se ve forzada a realizar una recarga de página completa en su lugar resolviendo la página a través del enrutamiento interactivo. La recarga de página completa obliga al componente raíz de nivel superior, normalmente el componente App (App.razor), a volver a representar desde el servidor, lo que permite que la aplicación cambie a otro modo de representación de nivel superior.

El método de extensión HttpContext.AcceptsInteractiveRouting permite al componente detectar si [ExcludeFromInteractiveRouting] se aplica a la página actual.

En el componente App, use el patrón en el ejemplo siguiente:

  • Páginas que no están anotadas con el valor predeterminado [ExcludeFromInteractiveRouting] para el modo de representación InteractiveServer con interactividad global. Puede reemplazar InteractiveServer por InteractiveWebAssembly o InteractiveAuto para especificar otro modo de representación global predeterminado.
  • Páginas anotadas con la adopción de SSR estática [ExcludeFromInteractiveRouting] (a PageRenderMode se le asigna 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;
}

Una alternativa al uso del método de extensión HttpContext.AcceptsInteractiveRouting es leer manualmente los metadatos del punto de conexión mediante HttpContext.GetEndpoint()?.Metadata.

Esta característica está cubierta por la documentación de referencia en los modelos de representación de Blazor de ASP.NET Core.

Inserción de constructores

Razor componentes admiten la inserción de constructores.

En el ejemplo siguiente, la clase parcial (código subyacente) inserta el servicio NavigationManager mediante un constructor principal:

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

Para obtener más información, vea Inserción de dependencias de Blazor de ASP.NET Core.

Compresión de Websocket para componentes de Servidor interactivo

De forma predeterminada, los componentes de Interactive Server habilitan la compresión para lasconexiones de WebSocket y establecen una frame-ancestorsdirectiva de directiva de seguridad de contenido (CSP) establecida en'self', que solo permite insertar la aplicación en un <iframe> origen desde el que se sirve la aplicación cuando se habilita la compresión o cuando se proporciona una configuración para el contexto de WebSocket.

La compresión se puede deshabilitar estableciendo ConfigureWebSocketOptions en null, lo que reduce la vulnerabilidad de la aplicación para atacar, pero puede dar lugar a una reducción del rendimiento:

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

Configure un frame-ancestorsCSP más estricto con un valor de 'none' (comillas simples necesarias), lo que permite la compresión de WebSocket, pero impide que los exploradores inserte la aplicación en cualquier <iframe>:

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

Para obtener más información, consulte los siguientes recursos:

Controlar eventos de composición de teclado en Blazor

La nueva propiedad KeyboardEventArgs.IsComposing indica si el evento de teclado forma parte de una sesión de composición. El seguimiento del estado de composición de los eventos de teclado es fundamental para controlar los métodos internacionales de entrada de caracteres.

Se ha agregado el parámetro OverscanCount a QuickGrid

El componente QuickGrid expone ahora una propiedad OverscanCount que especifica cuántas filas adicionales se representan antes y después de la región visible cuando la virtualización está habilitada.

El valor predeterminado de OverscanCount es 3. En el ejemplo siguiente, se aumenta OverscanCount a 4:

<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
    ...
</QuickGrid>

SignalR

En esta sección se describen las nuevas características de SignalR.

Compatibilidad con tipos polimórficos en concentradores SignalR

Los métodos de concentrador ahora pueden aceptar una clase base en lugar de la clase derivada para habilitar escenarios polimórficos. El tipo base debe estar anotado para permitir el 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; }
}

API mínimas

En esta sección se describen las nuevas características de las API mínimas.

Se ha agregado InternalServerError y InternalServerError<TValue> a TypedResults.

La clase TypedResults es un vehículo útil para devolver respuestas basadas en códigos de estado HTTP fuertemente tipados desde una API mínima. TypedResults ahora incluye métodos y tipos de fábrica para devolver las respuestas "Error interno del servidor 500" de los puntos de conexión. Este es un ejemplo que devuelve una respuesta 500:

var app = WebApplication.Create();

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

app.Run();

OpenAPI

Compatibilidad integrada con la generación de documentos de OpenAPI

La especificación OpenAPI es un estándar para describir las API HTTP. El estándar permite a los desarrolladores definir la forma de las API que se pueden conectar a generadores de cliente, generadores de servidores, herramientas de prueba, documentación, etc. En la versión preliminar de .NET 9, ASP.NET Core proporciona compatibilidad integrada para generar documentos de OpenAPI que representan API mínimas o basadas en controladores mediante el paquete Microsoft.AspNetCore.OpenApi.

El código resaltado siguiente llama a:

  • AddOpenApi para registrar las dependencias necesarias en el contenedor de inserción de dependencias de la aplicación.
  • MapOpenApi para registrar los puntos de conexión de OpenAPI necesarios en las rutas de la aplicación.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/hello/{name}", (string name) => $"Hello {name}"!);

app.Run();

Instale el paquete Microsoft.AspNetCore.OpenApi en el proyecto mediante el siguiente comando:

dotnet add package Microsoft.AspNetCore.OpenApi --prerelease

Ejecute la aplicación y vaya a openapi/v1.json para ver el documento de OpenAPI generado:

Documento de OpenAPI

Los documentos de OpenAPI también se pueden generar en tiempo de compilación al agregar el paquete Microsoft.Extensions.ApiDescription.Server:

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

En el archivo de proyecto de la aplicación, agregue lo siguiente:

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

Ejecute dotnet build e inspeccione el archivo JSON generado en el directorio del proyecto.

Generación de documentos de OpenAPI en tiempo de compilación

La generación de documentos OpenAPI integrada en ASP.NET Core admite varias personalizaciones y opciones. Proporciona transformadores de documentos y operaciones y tiene la capacidad de administrar varios documentos OpenAPI para la misma aplicación.

Para obtener más información sobre las nuevas funcionalidades del documento de OpenAPI de ASP.NET Core, consulte la nueva documentación de Microsoft.AspNetCore.OpenApi.

Autenticación y autorización

En esta sección se describen las nuevas características de autenticación y autorización.

Personalización de parámetros de OIDC y OAuth

Los gestores de autenticación OAuth y OIDC disponen ahora de una opción AdditionalAuthorizationParameters para facilitar la personalización de los parámetros de los mensajes de autorización que suelen incluirse como parte de la cadena de consulta de redireccionamiento. En .NET 8 y versiones anteriores, esto requiere una devolución de llamada personalizada OnRedirectToIdentityProvider o un método BuildChallengeUrl invalidado en un controlador personalizado. Este es un ejemplo de código de .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;
    };
});

El ejemplo anterior ahora se puede simplificar en el código siguiente:

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
    options.AdditionalAuthorizationParameters.Add("prompt", "login");
    options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});

Configuración de marcas de autenticación extendidas HTTP.sys

Ahora puede configurar las marcas HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING y HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL HTTP.sys utilizando las nuevas propiedades EnableKerberosCredentialCaching y CaptureCredentials en HTTP.sys AuthenticationManager para optimizar cómo se controla la autenticación de Windows. Por ejemplo:

webBuilder.UseHttpSys(options =>
{
    options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
    options.Authentication.EnableKerberosCredentialCaching = true;
    options.Authentication.CaptureCredentials = true;
});

Varios

En las secciones siguientes se describen varias características nuevas.

Nueva biblioteca de HybridCache

La API HybridCache cubre algunas lagunas en las API existentes IDistributedCache y IMemoryCache. También agrega nuevas funcionalidades, como:

  • Protección de "Stampede" para evitar capturas paralelas del mismo trabajo.
  • Serialización configurable.

HybridCache está diseñado como un reemplazo directo para el uso de IDistributedCache y IMemoryCache existentes, y proporciona una API sencilla para agregar código de almacenamiento en caché nuevo. Proporciona una API unificada para el almacenamiento en caché en proceso y fuera de proceso.

Para ver cómo se simplifica la API HybridCache, compárela con el código que usa IDistributedCache. Este es un ejemplo del aspecto que tiene el 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)
    { /* ... */ }
}

Eso supone mucho trabajo para conseguir hacerlo bien cada vez, incluyendo cosas como la serialización. Y en el escenario de pérdida de caché, podría acabar con varios subprocesos simultáneos, obtener una falta de caché, capturar todos los datos subyacentes, serializarlos y enviar esos datos a la memoria caché.

Para simplificar y mejorar este código con HybridCache, primero es necesario agregar la nueva biblioteca Microsoft.Extensions.Caching.Hybrid:

<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />

Registre el servicio HybridCache, como lo haría para registrar una implementación de IDistributedCache:

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

Ahora la mayoría de los problemas de almacenamiento en caché se pueden descargar en 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
        );
    }
}

Proporcionamos una implementación concreta de la clase abstracta HybridCache a través de la inserción de dependencias, pero está diseñada para que los desarrolladores puedan proporcionar implementaciones personalizadas de la API. La implementación HybridCache se ocupa de todo lo relacionado con el almacenamiento en caché, incluido el control de operaciones simultáneas. El token cancel aquí representa la cancelación combinada de todos los llamadores simultáneos, no solo la cancelación del autor de la llamada que podemos ver (es decir, token).

Los escenarios de alto rendimiento se pueden optimizar aún más mediante el patrón TState para evitar cierta sobrecarga de variables capturadas y devoluciones de llamada por instancia:

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 la implementación IDistributedCache configurada, si existe, para el almacenamiento en caché secundario fuera de proceso, por ejemplo, mediante Redis. Sin embargo, incluso sin IDistributedCache, el servicio HybridCache seguirá proporcionando protección de almacenamiento en caché en proceso y "stampede".

Nota sobre la reutilización de objetos

En el código existente típico que usa IDistributedCache, cada recuperación de un objeto de la memoria caché da como resultado la deserialización. Este comportamiento significa que cada llamador simultáneo obtiene una instancia independiente del objeto, que no puede interactuar con otras instancias. El resultado es la seguridad de subprocesos, ya que no hay ningún riesgo de modificaciones simultáneas en la misma instancia de objeto.

Dado que mucho uso de HybridCache se adaptará del código de IDistributedCache existente, HybridCache conserva este comportamiento de forma predeterminada para evitar introducir errores de simultaneidad. Sin embargo, un caso de uso determinado es intrínsecamente seguro para subprocesos:

  • Si los tipos que se almacenan en caché son inmutables.
  • Si el código no los modifica.

En tales casos, informe a HybridCache de que es seguro reutilizar instancias mediante:

  • Marcación del tipo como sealed. La palabra clave sealed en C# significa que la clase no se puede heredar.
  • Aplicación del atributo [ImmutableObject(true)] a él. El atributo [ImmutableObject(true)] indica que el estado del objeto no se puede cambiar después de crearlo.

Al reutilizar instancias, HybridCache puede reducir la sobrecarga de las asignaciones de CPU y objetos asociadas a la deserialización por llamada. Esto puede provocar mejoras de rendimiento en escenarios en los que los objetos almacenados en caché son grandes o a los que se accede con frecuencia.

Otras características de HybridCache

Al igual que IDistributedCache, HybridCache admite la eliminación por clave con un método RemoveKeyAsync.

HybridCache también proporciona API opcionales para implementaciones de IDistributedCache, con el fin de evitar asignaciones de byte[]. Esta característica se implementa mediante las versiones preliminares de los paquetes de Microsoft.Extensions.Caching.StackExchangeRedis y Microsoft.Extensions.Caching.SqlServer.

La serialización se configura como parte del registro del servicio, con compatibilidad con serializadores específicos de tipos y generalizados a través de los métodos WithSerializer y .WithSerializerFactory, encadenados desde la llamada AddHybridCache. De forma predeterminada, la biblioteca controla string y byte[] internamente, y usa System.Text.Json para todo lo demás, pero puede usar protobuf, xml o cualquier otra cosa.

HybridCache admite entornos de ejecución de .NET anteriores, hasta .NET Framework 4.7.2 y .NET Standard 2.0.

Para obtener más información sobre HybridCache, consulte Biblioteca HybridCache en ASP.NET Core

Mejoras en la página de excepciones para desarrolladores

La página de excepciones para desarrolladores de ASP.NET Core se muestra cuando una aplicación produce una excepción no controlada durante el desarrollo. La página de excepciones para desarrolladores proporciona información detallada sobre la excepción y la solicitud.

La versión preliminar 3 ha agregado metadatos de punto de conexión a la página de excepciones para desarrolladores. ASP.NET Core usa metadatos de punto de conexión para controlar el comportamiento del punto de conexión, como el enrutamiento, el almacenamiento en caché de respuestas, la limitación de velocidad, la generación de OpenAPI, etc. En la imagen siguiente, se muestra la nueva información de metadatos en la sección Routing de la página de excepciones para desarrolladores:

Nueva información de metadatos en la página de excepciones para desarrolladores

Al probar la página de excepciones para desarrolladores, se han identificado pequeñas mejoras de calidad de vida. Se han publicado en la versión preliminar 4:

  • Mejor ajuste de texto. Las cookies largas, los valores de cadena de consulta y los nombres de los métodos ya no agregan barras de desplazamiento horizontal en el navegador
  • Texto más grande que se encuentra en diseños modernos.
  • Tamaños de tabla más coherentes.

En la imagen animada siguiente, se muestra la nueva página de excepciones para desarrolladores:

La nueva página de excepciones para desarrolladores

Mejoras en la depuración de diccionarios

La visualización de la depuración de los diccionarios y otras colecciones de clave-valor tiene un diseño mejorado. La clave se muestra en la columna de clave del depurador, en lugar de estar concatenada con el valor. Las imágenes siguientes muestran la presentación antigua y nueva de un diccionario en el depurador.

Antes:

La experiencia anterior del depurador

Después:

La nueva experiencia del depurador

ASP.NET Core tiene muchas colecciones de clave-valor. Esta experiencia de depuración mejorada se aplica a:

  • Encabezados HTTP
  • Cadenas de consulta
  • Formularios
  • Cookies
  • Visualización de datos
  • Datos de ruta
  • Características

Corrección para 503 durante el reciclaje de aplicaciones en IIS

De forma predeterminada, ahora hay un retraso de 1 segundo entre cuando IIS recibe una notificación de reciclaje o apagado y cuando ANCM indica al servidor administrado que empiece a apagarse. El retraso se puede configurar a través de la variable de entorno ANCM_shutdownDelay o al establecer la configuración del controlador shutdownDelay. Ambos valores están en milisegundos. El retraso es principalmente para reducir la probabilidad de una carrera donde:

  • IIS no ha iniciado las solicitudes de puesta en cola para ir a la nueva aplicación.
  • ANCM comienza a rechazar nuevas solicitudes que entran en la aplicación antigua.

Las máquinas más lentas o con mayor uso de CPU pueden querer ajustar este valor para reducir la probabilidad de 503.

Ejemplo de configuración 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>

La corrección está en el módulo ANCM instalado globalmente que procede de la agrupación de hospedaje.

Optimización de la entrega de recursos web estáticos

La creación de aplicaciones web de rendimiento incluye la optimización de la entrega de recursos al explorador. Esto implica muchos aspectos como:

MapStaticAssets es un nuevo middleware que ayuda a optimizar la entrega de recursos estáticos en una aplicación. Está diseñado para trabajar con todos los marcos de interfaz de usuario, como Blazor, Razor Pages y MVC. Normalmente, es un reemplazo de drop-in para UseStaticFiles.

MapStaticAssets funciona combinando procesos en tiempo de compilación y publicación para recopilar información sobre todos los recursos estáticos de una aplicación. A continuación, la biblioteca en tiempo de ejecución utiliza esta información para servir estos archivos de forma eficaz al explorador.

MapStaticAssets puede reemplazar UseStaticFiles en la mayoría de las situaciones, pero está optimizado para atender los recursos de los que la aplicación tiene conocimiento en tiempo de compilación y publicación. Si la aplicación atiende recursos de otras ubicaciones, como el disco o los recursos incrustados, debe usarse UseStaticFiles.

MapStaticAssets proporciona las siguientes ventajas no encontradas con UseStaticFiles:

  • Compresión en tiempo de compilación para todos los recursos de la aplicación:
    • gzip durante el desarrollo y gzip + brotli durante la publicación.
    • Todos los recursos se comprimen con el objetivo de reducir el tamaño de los recursos al mínimo.
  • ETags basado en contenido: el Etags para cada recurso es la cadena codificada en Base64 del hash SHA-256 del contenido. Esto garantiza que el explorador solo vuelva a descargar un archivo si su contenido ha cambiado.

En la tabla siguiente se muestran los tamaños originales y comprimidos de los archivos CSS y JS en la plantilla predeterminada de Razor Pages:

Archivo Original Compressed % de reducción
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%

En la tabla siguiente se muestran los tamaños originales y comprimidos mediante la biblioteca de componentes de la interfaz de usuario de Fluent Blazor:

Archivo Original Compressed % de reducción
fluent.js 384 73 80.99%
fluent.css 94 11 88,30%
Total 478 84 82,43%

Para un total de 478 KB sin comprimir a 84 KB comprimidos.

En la tabla siguiente se muestran los tamaños originales y comprimidos mediante la biblioteca de componentes MudBlazorBlazor:

Archivo Original Compressed Reducción
MudBlazor.min.css 541 37,5 93,07%
MudBlazor.min.js 47,4 9.2 80,59%
Total 588,4 46,7 92,07%

La optimización se produce automáticamente cuando se usa MapStaticAssets. Cuando se agrega o actualiza una biblioteca, por ejemplo con JavaScript o CSS nuevos, los recursos se optimizan como parte de la compilación. La optimización es especialmente beneficiosa para los entornos móviles que pueden tener un ancho de banda menor o conexiones no confiables.

Habilitación de la compresión dinámica en el servidor frente al uso de MapStaticAssets

MapStaticAssets tiene las siguientes ventajas sobre la compresión dinámica en el servidor:

  • Es más sencillo porque no hay ninguna configuración específica del servidor.
  • Es más eficaz porque los recursos se comprimen en tiempo de compilación.
  • Permite al desarrollador dedicar más tiempo durante el proceso de compilación para asegurarse de que los recursos tengan el tamaño mínimo.

Tenga en cuenta la tabla siguiente que compara la compresión MudBlazor con la compresión dinámica de IIS y MapStaticAssets:

IIS gzip MapStaticAssets Reducción de MapStaticAssets
≅ 90 37,5 59 %