Compartir vía


Novedades de ASP.NET Core 8.0

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

Blazor

Interfaz de usuario web de pila completa

Con la versión de .NET 8, Blazor es un marco de interfaz de usuario web de pila completa para desarrollar aplicaciones que representan contenido en el nivel de componente o página con:

  • Representación de servidor estático (también denominada representación estática del lado servidor, SSR estático) para generar HTML estático en el servidor.
  • Representación interactiva del servidor (también denominada representación interactiva del lado servidor, SSR interactiva) para generar componentes interactivos con representación previa en el servidor.
  • Representación interactiva de WebAssembly (también denominada representación del lado cliente, CSR, que siempre se supone que es interactiva) para generar componentes interactivos en el cliente con representación previa en el servidor.
  • Representación automática interactiva (automática) para usar inicialmente el entorno de ejecución del lado servidor ASP.NET Core para la representación e interactividad del contenido. El entorno de ejecución de WebAssembly de .NET en el cliente se usa para la representación e interactividad posteriores después de descargar la agrupación de Blazor y se activa el entorno de ejecución de WebAssembly. La representación automática interactiva suele proporcionar la experiencia de inicio de la aplicación más rápida.

Los modos de representación interactiva también representan contenido prerrenderizado de forma predeterminada.

Para más información, consulte los siguientes artículos.

Los ejemplos de la documentación de Blazor se han actualizado para usarlos en Blazor Web App. Los ejemplos de Blazor Server permanecen en versiones de contenido para .NET 7 o versiones anteriores.

Nuevo artículo sobre bibliotecas de clases con representación estática del lado servidor (SSR estático)

Hemos agregado un nuevo artículo en el que se describe la autoría de la biblioteca de componentes en las bibliotecas de clases de Razor (RCL) con la representación estática del lado servidor (SSR estático).

Para obtener más información, consulte Bibliotecas de clases de ASP.NET Core (RCL) Razor con representación estática del lado servidor (SSR estático).

Nuevo artículo sobre problemas de almacenamiento en caché HTTP

Hemos agregado un nuevo artículo en el que se describen algunos de los problemas comunes de almacenamiento en caché HTTP que pueden producirse al actualizar aplicaciones de Blazor entre versiones principales y cómo solucionar problemas de almacenamiento en caché HTTP.

Para obtener más información, consulte Evitar problemas de almacenamiento en caché HTTP al actualizar aplicaciones ASP.NET Core Blazor.

Nueva plantilla de Blazor Web App

Hemos introducido una nueva plantilla de proyecto de Blazor: la plantilla de Blazor Web App. La nueva plantilla proporciona un único punto de partida para usar componentes de Blazor para compilar cualquier estilo de interfaz de usuario web. La plantilla combina los puntos fuertes de los modelos de hospedaje existentes Blazor Server y Blazor WebAssembly con las nuevas funcionalidades de Blazor agregadas en .NET 8: representación estática del lado servidor (SSR estático), representación de streaming, navegación y control de formularios mejorados, y la capacidad de agregar interactividad mediante Blazor Server o Blazor WebAssembly por componente.

Como parte de la unificación de los distintos modelos de hospedaje de Blazor en un solo modelo en .NET 8, también estamos consolidando el número de plantillas de proyecto de Blazor. Hemos quitado la plantilla de Blazor Server y la opción ASP.NET Core hospedado se ha quitado de la plantilla de Blazor WebAssembly. Ambos escenarios se representan mediante opciones al usar la plantilla de Blazor Web App.

Nota:

Las aplicaciones Blazor Server y Blazor WebAssembly existentes siguen siendo compatibles con .NET 8. Opcionalmente, estas aplicaciones se pueden actualizar para usar las nuevas características de Blazor de interfaz de usuario web de pila completa.

Para obtener más información sobre la nueva plantilla de Blazor Web App, consulta los siguientes artículos:

Nuevos inicializadores de JS para Blazor Web App

Para las aplicaciones de Blazor Server, Blazor WebAssembly y Blazor Hybrid:

  • beforeStart se usa para tareas como personalizar el proceso de carga, el nivel de registro y otras opciones.
  • afterStarted se usa para tareas como registrar agentes de escucha de eventos Blazor y tipos de eventos personalizados.

Los inicializadores JS heredados anteriores no se invocan de forma predeterminada en una Blazor Web App. Para Blazor Web App, se usa un nuevo conjunto de inicializadores JS: beforeWebStart, afterWebStarted, beforeServerStart, afterServerStarted, beforeWebAssemblyStart y afterWebAssemblyStarted.

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

División de instrucciones de representación previa e integración

En el caso de versiones anteriores de .NET, se ha tratado la representación previa e integración en un único artículo. Para simplificar y centrar nuestra cobertura, hemos dividido los temas en los siguientes artículos nuevos, que se han actualizado para .NET 8:

Conservación del estado del componente en una Blazor Web App

Puedes conservar y leer el estado del componente de una Blazor Web App mediante el servicio PersistentComponentState existente. Es útil para conservar el estado del componente durante la representación previa.

Las Blazor Web App conservan automáticamente cualquier estado a nivel de aplicación registrado durante la representación previa, lo que elimina la necesidad del asistente de etiquetas para conservar estados de componentes.

Control de formularios y enlace de modelos

Los componentes de Blazor ahora pueden controlar las solicitudes de formulario enviadas, incluidos el enlace de modelos y la validación de los datos de solicitud. Los componentes pueden implementar formularios con controladores de formulario independientes mediante la etiqueta HTML <form> estándar o mediante el componente existente EditForm.

El enlace del modelo de formulario de Blazor respeta los atributos del contrato de datos (por ejemplo, [DataMember] y [IgnoreDataMember]) para personalizar cómo se enlazan los datos del formulario al modelo.

La nueva compatibilidad antifraude se incluye en .NET 8. Un nuevo componente AntiforgeryToken representa un token antifraude como un campo oculto y el atributo [RequireAntiforgeryToken] habilita la protección antifraude. Si se produce un error en una comprobación antifraude, se devuelve una respuesta 400 (solicitud incorrecta) sin procesamiento de formularios. Las nuevas características antifraude están habilitadas de forma predeterminada para formularios basados en Editform y se pueden aplicar manualmente a los formularios HTML estándar.

Para obtener más información, vea Introducción a los formularios de ASP.NET CoreBlazor.

Navegación mejorada y control de formularios

La representación estática del lado servidor (SSR estática) normalmente realiza una actualización de página completa cada vez que el usuario navega a una página nueva o envía un formulario. En .NET 8, Blazor puede mejorar la navegación de páginas y el control de formularios interceptando la solicitud y realizando una solicitud de captura en su lugar. Blazor a continuación controla el contenido de la respuesta representada mediante la aplicación de revisiones en el DOM del explorador. La navegación mejorada y el control de formularios evita la necesidad de una actualización de página completa y conserva más el estado de la página, por lo que las páginas se cargan más rápido y sin problemas. La navegación mejorada está habilitada de forma predeterminada cuando se carga el script de Blazor (blazor.web.js). El control mejorado de formularios se puede habilitar opcionalmente para formularios específicos.

La nueva API de navegación mejorada permite actualizar la página actual llamando a NavigationManager.Refresh(bool forceLoad = false).

Para obtener más información, consulte las secciones siguientes del artículo de enrutamiento de Blazor:

Nuevo artículo sobre la representación estática con navegación mejorada para la interoperabilidad de JS

Algunas aplicaciones dependen de la interoperabilidad de JS para realizar tareas de inicialización específicas de cada página. Cuando se usa la característica de navegación mejorada de Blazor con páginas representadas estáticamente que realizan tareas de inicialización de interoperabilidad de JS, es posible que el JS específico de la página no se ejecute de nuevo según lo esperado cada vez que se produzca una navegación de página mejorada. En un nuevo artículo se explica cómo abordar este escenario en Blazor Web App:

ASP.NET Core Blazor JavaScript con representación estática del lado servidor (SSR estática)

Representación de streaming

Ahora puede transmitir actualizaciones de contenido en el flujo de respuesta al usar la representación estática del lado servidor (SSR estático) con Blazor. La representación de transmisiones puede mejorar la experiencia del usuario para las páginas que realizan tareas asincrónicas de larga duración para poder representar completamente el contenido en cuanto esté disponible.

Por ejemplo, para representar una página, es posible que tenga que realizar una consulta de base de datos de larga duración o una llamada API. Normalmente, las tareas asincrónicas ejecutadas como parte de la representación de una página deben completarse antes de enviar la respuesta representada, lo que puede retrasar la carga de la página. La representación de transmisiones representa inicialmente toda la página con contenido de marcador de posición mientras se ejecutan operaciones asincrónicas. Una vez completadas las operaciones asincrónicas, el contenido actualizado se envía al cliente en la misma conexión de respuesta y se revisa en el DOM. La ventaja de este enfoque es que el diseño principal de la aplicación se representa lo más rápido posible y la página se actualiza en cuanto el contenido esté listo.

Para obtener más información, consulte Representación de componentes de Razor de ASP.NET Core.

Inserción de servicios con clave en componentes

Blazor ahora admite la inserción de servicios con clave mediante el atributo [Inject]. Las claves permiten determinar el ámbito del registro y el consumo de servicios al usar la inserción de dependencias. Use la nueva propiedad InjectAttribute.Key para especificar la clave del servicio que se va a insertar:

[Inject(Key = "my-service")]
public IMyService MyService { get; set; }

La directiva @injectRazor no admite servicios con clave para esta versión, pero se hace un seguimiento del trabajo mediante Actualizar @inject para admitir servicios con clave (dotnet/razor #9286) para una versión futura de .NET.

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

Acceso a HttpContext como parámetro en cascada

Ahora puede acceder al objeto HttpContext actual como parámetro en cascada desde un componente de servidor estático:

[CascadingParameter]
public HttpContext? HttpContext { get; set; }

El acceso a HttpContext desde un componente de servidor estático puede ser útil para inspeccionar y modificar encabezados u otras propiedades.

Para ver un ejemplo que pasa el estado HttpContext, los tokens de acceso y actualización, a los componentes, consulte Escenarios de seguridad adicionales del lado del servidor de ASP.NET Core Blazor.

Representación de componentes de Razor fuera de ASP.NET Core

Puede representar componentes de Razor fuera del contexto de una solicitud HTTP. Puede representar componentes de Razor como HTML directamente en una cadena o secuencia independientemente del entorno de hospedaje de ASP.NET Core. Esto es conveniente para escenarios en los que desea generar fragmentos HTML, como para generar contenido para correos electrónicos o sitios estáticos.

Para obtener más información, consulte Representación de componentes de Razor fuera de ASP.NET Core.

Compatibilidad con secciones

Los nuevos componentesSectionOutlet y SectionContent de Blazor agregan compatibilidad para especificar salidas de contenido que se pueden rellenar más adelante. A menudo, las secciones se usan para definir marcadores de posición en diseños que, a continuación, se rellenan en páginas específicas. Se hace referencia a las secciones por un nombre único o mediante un identificador de objeto único.

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

Compatibilidad con páginas de error

Las Blazor Web App pueden definir una página de error personalizada para usarla con el middleware de control de excepciones de ASP.NET Core. La plantilla de proyecto de Blazor Web App incluye una página de error predeterminada (Components/Pages/Error.razor) con contenido similar al que se usa en las aplicaciones de MVC y Razor Pages. Cuando la página de error se representa en respuesta a una solicitud del middleware de control de excepciones, la página de error siempre se representa como un componente de servidor estático, incluso si la interactividad está habilitada de otro modo.

Error.razor en origen de referencia 8.0

QuickGrid

El componente QuickGrid de Blazor ya no es experimental y ahora forma parte del marco de Blazor en .NET 8.

QuickGrid es un componente de cuadrícula de alto rendimiento para mostrar datos en forma tabular. QuickGrid se ha creado para ser una manera sencilla y cómoda de mostrar los datos, a la vez que proporciona características eficaces, como la ordenación, el filtrado, la paginación y la virtualización.

Para obtener más información, consulte Componente QuickGrid Blazor de ASP.NET Core.

Ruta a elementos con nombre

Blazor ahora admite el uso del enrutamiento del lado cliente para navegar a un elemento HTML específico en una página mediante fragmentos de dirección URL estándar. Si especifica un identificador para un elemento HTML mediante el atributo estándar id, Blazor se desplaza correctamente a ese elemento cuando el fragmento de dirección URL coincide con el identificador de elemento.

Para más información, vea Enrutamiento y navegación de Blazor de ASP.NET Core.

Valores en cascada de nivel raíz

Los valores en cascada de nivel raíz se pueden registrar para toda la jerarquía de componentes. Se admiten los valores en cascada con nombre y las suscripciones para las notificaciones de actualización.

Para más información, vea Valores y parámetros en cascada de Blazor en ASP.NET Core.

Virtualización de contenido vacío

Use el parámetro EmptyContent del componente Virtualize para proporcionar contenido cuando el componente se haya cargado y Items esté vacío o el valor de ItemsProviderResult<T>.TotalItemCount sea cero.

Para más información, vea Virtualización de componentes Razor de ASP.NET Core.

Cierre de los circuitos cuando no quedan componentes de servidor interactivos

Los componentes de servidor interactivos controlan los eventos de la interfaz de usuario web mediante una conexión en tiempo real con el explorador denominado circuito. Un circuito y su estado asociado se configuran cuando se representa un componente de servidor interactivo raíz. El circuito se cierra cuando no quedan componentes de servidor interactivos en la página, lo que libera los recursos del servidor.

Supervisión de la actividad del circuito de SignalR

Ahora puede supervisar la actividad del circuito entrante en aplicaciones del lado servidor mediante el nuevo método CreateInboundActivityHandler en CircuitHandler. La actividad de circuito entrante es cualquier actividad enviada desde el explorador al servidor, como eventos de interfaz de usuario o llamadas de interoperabilidad de JavaScript a .NET.

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

Rendimiento en tiempo de ejecución más rápido con Jiterpreter

Jiterpreter es una nueva característica en tiempo de ejecución en .NET 8 que permite la compatibilidad parcial con la compilación Just-In-Time (JIT) al ejecutarse en WebAssembly para lograr un rendimiento en tiempo de ejecución mejorado.

Para obtener más información, vea Hospedaje e implementación de Blazor WebAssembly en ASP.NET Core.

SIMD con compilación anticipada y control de excepciones

La compilación anticipada (AOT) de Blazor WebAssembly ahora usa el SIMD de ancho fijo de WebAssembly y el control de excepciones de WebAssembly de forma predeterminada para mejorar el rendimiento en tiempo de ejecución.

Para más información, consulte los siguientes artículos.

Empaquetado de Webcil para web

Webcil es un empaquetado para web de ensamblados de .NET que quita contenido específico de la ejecución nativa de Windows para evitar problemas al implementar en entornos que bloquean la descarga o el uso de archivos de .dll. Webcil está habilitado de forma predeterminada para las aplicaciones de Blazor WebAssembly.

Para obtener más información, vea Hospedaje e implementación de Blazor WebAssembly en ASP.NET Core.

Nota:

Antes del lanzamiento de .NET 8, las instrucciones de Diseño de implementación para aplicaciones Blazor WebAssembly hospedadas en ASP.NET Core abordan los entornos que impiden que los clientes descarguen y ejecuten archivos DLL con un enfoque de agrupación de varias partes. En .NET 8 o posterior, Blazor usa el formato de archivo Webcil para solucionar este problema. La agrupación de varias partes mediante el paquete NuGet experimental descrito en el artículo Diseño de implementación de WebAssembly no se admite para aplicaciones Blazor en .NET 8 o posterior. Puede obtener más información en Mejorar el paquete Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle para definir un formato de agrupación personalizado (dotnet/aspnetcore #36978). Si desea seguir usando el paquete de agrupación de varias partes en aplicaciones de .NET 8 o posteriores, puede usar las instrucciones del artículo para crear su propio paquete NuGet de agrupación de varias partes, pero no será compatible con Microsoft.

Mejoras en la depuración de Blazor WebAssembly

Al depurar .NET en WebAssembly, el depurador ahora descarga datos de símbolos de ubicaciones de símbolos configuradas en las preferencias de Visual Studio. Esto mejora la experiencia de depuración para las aplicaciones que usan paquetes NuGet.

Ahora puede depurar aplicaciones de Blazor WebAssembly con Firefox. La depuración de aplicaciones de Blazor WebAssembly requiere configurar el explorador para la depuración remota y conectarse al explorador mediante las herramientas de desarrollo del explorador a través del proxy de depuración WebAssembly de .NET. La depuración en Firefox desde Visual Studio no se admite en este momento.

Para más información, vea Depuración de aplicaciones de BlazorASP.NET Core.

Compatibilidad con la directiva de seguridad de contenido

Blazor WebAssembly ya no requiere habilitar el origen del script de unsafe-eval al especificar una directiva de seguridad de contenido (CSP).

Para más información, vea Aplicación de una directiva de seguridad de contenido para Blazor de ASP.NET Core.

Control de las excepciones detectadas fuera del ciclo de vida de un componente Razor

Usa ComponentBase.DispatchExceptionAsync en un componente Razor para procesar las excepciones producidas fuera de la pila de llamadas del ciclo de vida del componente. Esto permite que el código del componente trate las excepciones como si fueran excepciones del método de ciclo de vida. A partir de entonces, los mecanismos de control de errores de Blazor, como los límites de error, pueden procesar las excepciones.

Para más información, vea Control de errores en aplicaciones Blazor de ASP.NET Core.

Configuración del tiempo de ejecución de WebAssembly de .NET

El entorno de ejecución de WebAssembly de .NET ahora se puede configurar para el inicio de Blazor.

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

Configuración de tiempos de espera de conexión en HubConnectionBuilder

Las soluciones alternativas anteriores para configurar los tiempos de espera de conexión del centro se pueden reemplazar por la configuración formal del tiempo de espera del generador de conexiones de concentradores SignalR.

Para obtener más información, vea lo siguiente:

Plantillas de proyecto con Open Iconic

Las plantillas de proyecto de Blazor ya no dependen de Open Iconic para los iconos.

Compatibilidad con eventos de cierre y cancelación de cuadros de diálogo

Blazor ahora admite los eventos cancel y close en el elemento HTML dialog.

En el ejemplo siguiente:

  • Se llama a OnClose cuando se cierra el cuadro de diálogo my-dialog con el botón Cerrar.
  • Se llama a OnCancel cuando se cancela el cuadro de diálogo con la tecla Esc. Cuando se descarta un cuadro de diálogo HTML con la tecla Esc, se desencadenan los eventos cancel y close.
<div>
    <p>Output: @message</p>

    <button onclick="document.getElementById('my-dialog').showModal()">
        Show modal dialog
    </button>

    <dialog id="my-dialog" @onclose="OnClose" @oncancel="OnCancel">
        <p>Hi there!</p>

        <form method="dialog">
            <button>Close</button>
        </form>
    </dialog>
</div>

@code {
    private string? message;

    private void OnClose(EventArgs e) => message += "onclose, ";

    private void OnCancel(EventArgs e) => message += "oncancel, ";
}

IU de BlazorIdentity

Blazor admite la generación de una interfaz de usuario completa basada en BlazorIdentity al elegir la opción de autenticación para cuentas individuales. Puedes seleccionar la opción de cuentas individuales en el cuadro de diálogo de nuevo proyecto para Blazor Web App desde Visual Studio o pasar la opción -au|--auth establecida en Individual desde la línea de comandos al crear un nuevo proyecto.

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

Protección de Blazor WebAssembly con ASP.NET Core Identity

La documentación de Blazor incluye un nuevo artículo y una aplicación de ejemplo para cubrir la protección de una aplicación independiente de Blazor WebAssembly con ASP.NET Core Identity.

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

Blazor Server con enrutamiento Yarp

El enrutamiento y la vinculación en profundidad para Blazor Server con Yarp funcionan correctamente en .NET 8.

Para obtener más información, consulte Migración de ASP.NET Core 7.0 a 8.0.

Varias Blazor Web App por proyecto de servidor

La compatibilidad con varias Blazor Web App por proyecto de servidor se considerará para .NET 10 (noviembre de 2025).

Para obtener más información, consulta Compatibilidad con varias aplicaciones web Blazor por proyecto de servidor (dotnet/aspnetcore #52216).

Blazor Hybrid

En los artículos siguientes se documentan los cambios de Blazor Hybrid en .NET 8:

El atributo [Parameter] ya no es necesario cuando al proporcionarlo desde la cadena de consulta

El atributo [Parameter] ya no es necesario al proporcionar un parámetro de la cadena de consulta:

- [Parameter]
  [SupplyParameterFromQuery]

SignalR

Nuevo enfoque para establecer el tiempo de espera del servidor y el intervalo de Mantener conexión

ServerTimeout (valor predeterminado: 30 segundos) y KeepAliveInterval (valor predeterminado: 15 segundos) se pueden establecer directamente en HubConnectionBuilder.

Enfoque anterior para los clientes de JavaScript

En el ejemplo siguiente se muestra la asignación de valores que son el doble de los valores predeterminados en ASP.NET Core 7.0 o versiones anteriores:

var connection = new signalR.HubConnectionBuilder()
  .withUrl("/chatHub")
  .build();

connection.serverTimeoutInMilliseconds = 60000;
connection.keepAliveIntervalInMilliseconds = 30000;

Nuevo enfoque para los clientes de JavaScript

En el ejemplo siguiente se muestra el nuevo enfoque para asignar valores que son el doble de los valores predeterminados en ASP.NET Core 8.0 o versiones posteriores:

var connection = new signalR.HubConnectionBuilder()
  .withUrl("/chatHub")
  .withServerTimeout(60000)
  .withKeepAlive(30000)
  .build();

Enfoque anterior para el cliente de JavaScript de una aplicación Blazor Server

En el ejemplo siguiente se muestra la asignación de valores que son el doble de los valores predeterminados en ASP.NET Core 7.0 o versiones anteriores:

Blazor.start({
  configureSignalR: function (builder) {
    let c = builder.build();
    c.serverTimeoutInMilliseconds = 60000;
    c.keepAliveIntervalInMilliseconds = 30000;
    builder.build = () => {
      return c;
    };
  }
});

Nuevo enfoque para el cliente JavaScript de la aplicación Blazor del lado del servidor

En el ejemplo siguiente se muestra el nuevo enfoque para asignar valores que son el doble de los valores predeterminados en ASP.NET Core 8.0 o versiones posteriores para Blazor Web App y Blazor Server.

Blazor Web App:

Blazor.start({
  circuit: {
    configureSignalR: function (builder) {
      builder.withServerTimeout(60000).withKeepAliveInterval(30000);
    }
  }
});

Blazor Server:

Blazor.start({
  configureSignalR: function (builder) {
    builder.withServerTimeout(60000).withKeepAliveInterval(30000);
  }
});

Enfoque anterior para los clientes de .NET

En el ejemplo siguiente se muestra la asignación de valores que son el doble de los valores predeterminados en ASP.NET Core 7.0 o versiones anteriores:

var builder = new HubConnectionBuilder()
    .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
    .Build();

builder.ServerTimeout = TimeSpan.FromSeconds(60);
builder.KeepAliveInterval = TimeSpan.FromSeconds(30);

builder.On<string, string>("ReceiveMessage", (user, message) => ...

await builder.StartAsync();

Nuevo enfoque para los clientes de .NET

En el ejemplo siguiente se muestra el nuevo enfoque para asignar valores que son el doble de los valores predeterminados en ASP.NET Core 8.0 o versiones posteriores:

var builder = new HubConnectionBuilder()
    .WithUrl(Navigation.ToAbsoluteUri("/chathub"))
    .WithServerTimeout(TimeSpan.FromSeconds(60))
    .WithKeepAliveInterval(TimeSpan.FromSeconds(30))
    .Build();

builder.On<string, string>("ReceiveMessage", (user, message) => ...

await builder.StartAsync();

Reconexión con estado de SignalR

La reconexión con estado de SignalR reduce el tiempo de inactividad percibido de los clientes que tienen una desconexión temporal en su conexión de red, como al cambiar las conexiones de red o una breve pérdida temporal de acceso.

La reconexión con estado lo consigue gracias a:

  • Almacenamiento temporal en búfer de los datos en el servidor y el cliente.
  • Confirmación de los mensajes recibidos (ACK-ing) tanto por el servidor como por el cliente.
  • Reconocimiento de cuándo se devuelve una conexión y se reproducen mensajes que pueden haberse enviado mientras la conexión estaba inactiva.

La reconexión con estado está disponible en ASP.NET Core 8.0 y versiones posteriores.

Opte por la reconexión con estado tanto en el punto de conexión del centro de servidores como en el cliente:

  • Actualice la configuración del punto de conexión del centro de servidores para habilitar la opción AllowStatefulReconnects:

    app.MapHub<MyHub>("/hubName", options =>
    {
        options.AllowStatefulReconnects = true;
    });
    

    Opcionalmente, el tamaño máximo del búfer en bytes permitido por el servidor se puede establecer globalmente o para un centro específico con la opción StatefulReconnectBufferSize:

    La opción StatefulReconnectBufferSize establecida globalmente:

    builder.AddSignalR(o => o.StatefulReconnectBufferSize = 1000);
    

    La opción StatefulReconnectBufferSize establecida para un centro específico:

    builder.AddSignalR().AddHubOptions<MyHub>(o => o.StatefulReconnectBufferSize = 1000);
    

    La opción StatefulReconnectBufferSize es opcional con un valor predeterminado de 100 000 bytes.

  • Actualice el código de cliente de JavaScript o TypeScript para habilitar la opción withStatefulReconnect:

    const builder = new signalR.HubConnectionBuilder()
      .withUrl("/hubname")
      .withStatefulReconnect({ bufferSize: 1000 });  // Optional, defaults to 100,000
    const connection = builder.build();
    

    La opción bufferSize es opcional con un valor predeterminado de 100 000 bytes.

  • Actualice el código de cliente de .NET para habilitar la opción WithStatefulReconnect:

      var builder = new HubConnectionBuilder()
          .WithUrl("<hub url>")
          .WithStatefulReconnect();
      builder.Services.Configure<HubConnectionOptions>(o => o.StatefulReconnectBufferSize = 1000);
      var hubConnection = builder.Build();
    

    La StatefulReconnectBufferSize opción es opcional con un valor predeterminado de 100 000 bytes.

Para obtener más información, consulte Configuración de la reconexión con estado.

API mínimas

En esta sección se describen las nuevas características de las API mínimas. Consulte también la sección sobre AOT nativo para obtener más información relevante de las API mínimas.

Referencia cultural de invalidación de usuario

A partir de ASP.NET Core 8.0, la propiedad RequestLocalizationOptions.CultureInfoUseUserOverride permite a la aplicación decidir si se debe usar o no la configuración no predeterminada de Windows para las propiedades CultureInfo DateTimeFormat y NumberFormat. Esto no afecta a Linux. Esto corresponde directamente a UseUserOverride.

    app.UseRequestLocalization(options =>
    {
        options.CultureInfoUseUserOverride = false;
    });

Enlace a formularios

Ahora se admite el enlace explícito a valores de formulario mediante el atributo [FromForm]. Los parámetros enlazados a la solicitud con [FromForm] incluyen un token antifalsificación. El token antifalsificación se valida cuando se procesa la solicitud.

También se admite el enlace inferido a formularios que usan los tipos IFormCollection, IFormFile y IFormFileCollection. Los metadatos de OpenAPI se deducen para que los parámetros de formulario admitan la integración con la interfaz de usuario de Swagger.

Para más información, consulte:

Ahora se admite el enlace desde formularios para:

  • Colecciones, por ejemplo, Lista y Diccionario
  • Tipos complejos, por ejemplo, Todo o Project

Para obtener más información, vea Enlazar a colecciones y tipos complejos de formularios.

Antifalsificación con API mínimas

Esta versión agrega un middleware para validar tokens antifalsificación, que se usan para mitigar los ataques de falsificación de solicitudes entre sitios. Llame a AddAntiforgery para registrar servicios de antifalsificación en la inserción de dependencias. WebApplicationBuilder agrega automáticamente el middleware cuando los servicios de antifalsificación se han registrado en el contenedor de inserción de dependencias. Los tokens de antifalsificación se usan para mitigar losataques de falsificación de solicitud entre sitios.

var builder = WebApplication.CreateBuilder();

builder.Services.AddAntiforgery();

var app = builder.Build();

app.UseAntiforgery();

app.MapGet("/", () => "Hello World!");

app.Run();

Middleware de antifalsificación:

El token de antifalsificación solo se valida si:

  • El punto de conexión contiene metadatos que implementan IAntiforgeryMetadata donde RequiresValidation=true.
  • El método HTTP asociado al punto de conexión es un método HTTP pertinente. Los métodos pertinentes son todos los métodos HTTP, excepto TRACE, OPTIONS, HEAD y GET.
  • La solicitud está asociada a un punto de conexión válido.

Para más información, consulte Antifalsificación con API mínima.

Nueva interfaz de IResettable en ObjectPool

Microsoft.Extensions.ObjectPool proporciona compatibilidad con la agrupación de instancias de objeto en memoria. Las aplicaciones pueden usar un grupo de objetos si los valores son costosos de asignar o inicializar.

En esta versión, facilitamos el uso de la reserva de objetos agregando la interfazIResettable. A menudo, los tipos reutilizables deben restablecerse a un estado predeterminado entre los usos. IResettable restablece automáticamente cuando se devuelven a una reserva de objetos.

Para obtener más información, consulte Ejemplo ObjectPool.

AOT nativo

Se ha agregado compatibilidad con .NET ahead-of-time (AOT) nativo. Las aplicaciones publicadas mediante AOT pueden tener un rendimiento considerablemente mejor: aplicaciones más pequeñas, menor uso de memoria y tiempo de inicio más rápido. AOT nativa es compatible actualmente con gRPC, API mínima y aplicaciones de servicio de trabajo. Para obtener más información, consulte Compatibilidad de ASP.NET Core con AOT nativo y Tutorial: Publicación de una aplicación de ASP.NET Core con AOT nativo. Para obtener información sobre los problemas conocidos de ASP.NET Core y la compatibilidad con AOT nativo, consulte el problema dotnet/core #8288 en GitHub.

Bibliotecas y AOT nativo

Muchas de las bibliotecas populares que se usan en los proyectos de ASP.NET Core actualmente tienen algunos problemas de compatibilidad cuando se utilizan en un proyecto que tiene como destino AOT nativo, por ejemplo:

  • Uso de la reflexión para inspeccionar y detectar tipos.
  • Carga condicional de bibliotecas en tiempo de ejecución.
  • Generar código sobre la marcha para implementar la funcionalidad.

Las bibliotecas que usan estas características dinámicas se deben actualizar para poder trabajar con AOT nativo. Se pueden actualizar usando herramientas como generadores de origen de Roslyn.

A los creadores de bibliotecas que quieran admitir AOT nativo se les recomienda lo siguiente:

Nueva plantilla de proyecto

La nueva plantilla de proyecto ASP.NET Core Web API (AOT nativo) (nombre cortowebapiaot) crea un proyecto con la publicación AOT habilitada. Para obtener información, consulte la Plantilla API Web (AOT nativo).

Nuevo método CreateSlimBuilder

El método CreateSlimBuilder() usado en la plantilla de API web (AOT nativo) inicializa WebApplicationBuilder con las características mínimas de ASP.NET Core necesarias para ejecutar una aplicación. El método CreateSlimBuilder incluye las siguientes características que, por lo general, son necesarias para una experiencia de desarrollo eficaz:

  • Configuración del archivo JSON para appsettings.json y appsettings.{EnvironmentName}.json.
  • Configuración de secretos de usuario
  • Registro de consolas
  • Configuración del registro

Para más información, consulte El método CreateSlimBuilder.

Nuevo método CreateEmptyBuilder

Hay otro nuevo método de fábrica WebApplicationBuilder para crear aplicaciones pequeñas que solo contengan características necesarias: WebApplication.CreateEmptyBuilder(WebApplicationOptions options). Este WebApplicationBuilder se crea sin ningún comportamiento integrado. La aplicación que compila contiene solo los servicios y el middleware que están configurados explícitamente.

Este es un ejemplo del uso de esta API para crear una aplicación web pequeña:

var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());
builder.WebHost.UseKestrelCore();

var app = builder.Build();

app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Hello, World!");
    await next(context);
});

Console.WriteLine("Running...");
app.Run();

La publicación de este código con AOT nativo mediante .NET 8 (versión preliminar 7) en una máquina linux-x64 da como resultado un ejecutable nativo independiente de aproximadamente 8.5 MB.

Tamaño reducido de la aplicación con compatibilidad con HTTPS configurable

Hemos reducido aún más el tamaño binario nativo de AOT para las aplicaciones que no necesitan compatibilidad con HTTPS o HTTP/3. No usar HTTPS o HTTP/3 es común para las aplicaciones que se ejecutan detrás de un proxy de terminación TLS (por ejemplo, hospedado en Azure). El nuevo método WebApplication.CreateSlimBuilder omite esta funcionalidad de forma predeterminada. Se puede agregar llamando a builder.WebHost.UseKestrelHttpsConfiguration() para HTTPS o builder.WebHost.UseQuic() para HTTP/3. Para más información, consulte El método CreateSlimBuilder.

Serialización JSON de tipos IAsyncEnumerable<T> generados por el compilador

Se agregaron nuevas características a System.Text.Json para admitir mejor AOT nativo. Estas nuevas características agregan funcionalidades para el modo de generación de origen de System.Text.Json, porque AOT no admite la reflexión.

Una de las nuevas características es la compatibilidad con la serialización JSON de implementaciones de IAsyncEnumerable<T> realizadas por el compilador de C#. Esta compatibilidad abre su uso en proyectos de ASP.NET Core configurados para publicar AOT nativo.

Esta API es útil en escenarios en los que un controlador de ruta usa yield return para devolver una enumeración de forma asincrónica. Por ejemplo, para materializar filas de una consulta de base de datos. Para más información, consulte Compatibilidad con tipos indescriptibles en el anuncio de .NET 8 Preview 4.

Para información sobre otras mejoras en la generación de código fuente de System.Text.Json, consulte Mejoras de serialización en .NET 8.

API de nivel superior anotadas para las advertencias de recorte

Ahora se anotan los principales puntos de entrada a subsistemas que no funcionan de forma confiable con AOT nativo. Cuando se llama a estos métodos desde una aplicación con AOT nativo habilitado, se proporciona una advertencia. Por ejemplo, el siguiente código genera una advertencia en la invocación de AddControllers, porque esta API no es segura para recortes y no es compatible con AOT nativo.

Ventana de Visual Studio que muestra el mensaje de advertencia IL2026 sobre el método AddControllers que indica que MVC no admite actualmente AOT nativo.

Generador de delegados de solicitud

Para que las API mínimas sean compatibles con AOT nativo, hemos incluido el generador delegado de solicitudes (RDG). RDG es un generador de código fuente que hace lo mismo que RequestDelegateFactory (RDF). Es decir, convierte los distintos MapGet(), MapPost() y llamadas similares en instancias de RequestDelegate asociadas a las rutas especificadas. Pero, en lugar de hacerlo en la memoria en una aplicación cuando se inicia, RDG lo hace en tiempo de compilación y genera código de C# directamente en el proyecto. RDG:

  • Elimina la generación de este código en tiempo de ejecución.
  • Garantiza que los tipos que se usan en las API se puedan analizar de forma estática con la cadena de herramientas de AOT nativo.
  • Garantiza que no se recorte el código necesario.

Estamos trabajando para asegurarnos de que RDG admita todas las características de API mínimas que sea posible y, por tanto, sea compatible con AOT nativo.

RDG se habilita automáticamente en un proyecto cuando se habilita la publicación con AOT nativo. RDG se puede habilitar manualmente incluso cuando no se usa AOT nativo, estableciendo <EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator> en el archivo de proyecto. Esto puede ser útil cuando se evalúa inicialmente el nivel de preparación de un proyecto para AOT nativo o para reducir el tiempo de inicio de una aplicación.

Rendimiento mejorado mediante interceptores

El generador de delegados de solicitud usa la nueva característica del compilador de interceptores de C# 12 para admitir llamadas interceptantes a métodos Map de API mínimos con variantes generadas estáticamente en tiempo de ejecución. El uso de interceptores da como resultado un mayor rendimiento de inicio para las aplicaciones compiladas con PublishAot.

Registro y control de excepciones en las API mínimas generadas en tiempo de compilación

Las API mínimas generadas en tiempo de ejecución admiten el registro automático (o la iniciación de excepciones en entornos de desarrollo) cuando se produce un error en el enlace de parámetros. .NET 8 presenta la misma compatibilidad con las API generadas en tiempo de compilación a través del generador de delegados de solicitudes (RDG). Para obtener más información, consulte Registro y control de excepciones en las API mínimas generadas en tiempo de compilación.

AOT y System.Text.Json

Las API mínimas están optimizadas para recibir y devolver cargas de JSON con System.Text.Json, por lo que también se aplican los requisitos de compatibilidad de JSON y AOT nativo. La compatibilidad con AOT nativo requiere el uso del generador de código fuente System.Text.Json. Todos los tipos aceptados como parámetros en delegados de solicitud o devueltos en las API mínimas deben configurarse en un objeto JsonSerializerContext registrado a través de la inserción de dependencias de ASP.NET Core, por ejemplo:

// Register the JSON serializer context with DI
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

...

// Add types used in the minimal API app to source generated JSON serializer content
[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{

}

Para obtener más información acerca de la API TypeInfoResolverChain, vea los siguientes recursos:

Bibliotecas y AOT nativo

Muchas de las bibliotecas comunes disponibles para los proyectos de ASP.NET Core actualmente tienen algunos problemas de compatibilidad si se usan en un proyecto destinado a AOT nativo. Las bibliotecas más populares suelen basarse en la funcionalidad dinámica de reflexión de .NET para inspeccionar y detectar tipos, cargar bibliotecas condicionalmente en tiempo de ejecución y generar código sobre la marcha para implementar su funcionalidad. Estas bibliotecas deben actualizarse para poder trabajar con AOT nativo con herramientas como los generadores de código fuente de Roslyn.

Se recomienda a los creadores de bibliotecas que deseen aprender más sobre cómo preparar sus bibliotecas para AOT nativo que empiecen preparando las bibliotecas para poder recortarlas y obteniendo más información sobre los requisitos de compatibilidad para AOT nativo.

Servidores Kestrel y HTTP.sys

Hay varias características nuevas para Kestrel y HTTP.sys.

Compatibilidad con canalizaciones con nombre en Kestrel

Las canalizaciones con nombre son una tecnología popular para crear comunicación entre procesos (IPC) entre aplicaciones de Windows. Ahora puede compilar un servidor IPC usando .NET, Kestrel y canalizaciones con nombre.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenNamedPipe("MyPipeName");
});

Para obtener más información sobre esta característica y cómo usar .NET y gRPC para crear un servidor y un cliente IPC, consulte Comunicación entre procesos con gRPC.

Mejoras de rendimiento en el transporte de canalizaciones con nombre

Hemos mejorado el rendimiento de la conexión de canalización con nombre. KestrelEl transporte de canalización con nombre ahora acepta conexiones en paralelo y reutiliza NamedPipeServerStream instancias.

Tiempo para crear 100 000 conexiones:

  • Antes: 5916 segundos
  • Después: 2374 segundos

Compatibilidad con HTTP/2 sobre TLS (HTTPS) en macOS en Kestrel

.NET 8 agrega compatibilidad con Application-Layer Protocol Negotiation (ALPN) a macOS. ALPN es una característica TLS que se usa para negociar qué protocolo HTTP usará una conexión. Por ejemplo, ALPN permite que los exploradores y otros clientes HTTP soliciten una conexión HTTP/2. Esta característica es especialmente útil para las aplicaciones gRPC, que requieren HTTP/2. Para más información, consulte Uso de HTTP/2 con el servidor web Kestrel de ASP.NET Core.

Inspección de archivos de certificado en Kestrel

Ahora se supervisan los certificados TLS configurados por ruta de acceso cuando se pasa reloadOnChange a KestrelServerOptions.Configure(). Un cambio en el archivo de certificado se trata del mismo modo que un cambio en la ruta de acceso configurada (es decir, los puntos de conexión se vuelven a cargar).

Tenga en cuenta que las eliminaciones de archivos no se realizan específicamente, ya que surgen transitoriamente y bloquearían el servidor si no son transitorios.

Advertencia cuando no se usarán los protocolos HTTP especificados

Si TLS está deshabilitado y HTTP/1.x está disponible, se deshabilitan HTTP/2 y HTTP/3, incluso si se han especificado. Esto puede producir algunas sorpresas desagradables, por lo que hemos agregado una advertencia para informar de esto cuando ocurra.

Claves de configuración HTTP_PORTS y HTTPS_PORTS

A menudo, las aplicaciones y los contenedores solo reciben un puerto de escucha, como el 80, sin restricciones adicionales como el host o la ruta de acceso. HTTP_PORTS y HTTPS_PORTS son claves de configuración que permiten especificar los puertos de escucha de los servidores Kestrel y HTTP.sys. Estas claves se pueden definir con los prefijos de variable de entorno DOTNET_ o ASPNETCORE_, o se pueden especificar directamente mediante cualquier otra entrada de configuración, como appsettings.json. Cada una es una lista delimitada por punto y coma de valores de puerto. Por ejemplo:

ASPNETCORE_HTTP_PORTS=80;8080
ASPNETCORE_HTTPS_PORTS=443;8081

Esta es una forma abreviada de lo siguiente, que especifica el esquema (HTTP o HTTPS) y cualquier host o IP:

ASPNETCORE_URLS=http://*:80/;http://*:8080/;https://*:443/;https://*:8081/

Para más información, consulte Configuración de puntos de conexión para el servidor web Kestrel de ASP.NET Core e Implementación del servidor web de HTTP.sys en ASP.NET Core.

Nombre de host de SNI en ITlsHandshakeFeature

El nombre de host de la indicación de nombre de servidor (SNI) se expone ahora en la propiedad HostName de la interfaz ITlsHandshakeFeature.

SNI forma parte del proceso de protocolo de enlace TLS. Permite a los clientes especificar el nombre de host al que intentan conectarse cuando el servidor hospeda varios hosts virtuales o dominios. Para presentar el certificado de seguridad correcto durante el proceso de protocolo de enlace, el servidor debe conocer el nombre de host seleccionado para cada solicitud.

Normalmente, el nombre de host solo se controla dentro de la pila TLS y se usa para seleccionar el certificado coincidente. Sin embargo, al exponerlo, otros componentes de una aplicación pueden usar esa información con fines como diagnósticos, limitación de velocidad, enrutamiento y facturación.

Exponer el nombre de host es útil para los servicios a gran escala que administran miles de enlaces SNI. Esta característica puede mejorar significativamente la eficacia de la depuración durante las escalaciones de los clientes. Una mayor transparencia permite una resolución de problemas más rápida y una confiabilidad mejorada del servicio.

Para saber más, consulte ITlsHandshakeFeature.HostName.

IHttpSysRequestTimingFeature

IHttpSysRequestTimingFeature proporciona información detallada de control del tiempo para las solicitudes cuando se usan el servidor HTTP.sys y el hospedaje dentro de proceso con IIS:

  • Las marcas de tiempo se obtienen con QueryPerformanceCounter.
  • La frecuencia de las marcas de tiempo se puede obtener con QueryPerformanceFrequency.
  • El índice de control del tiempo se puede convertir en HttpSysRequestTimingType para saber qué representa el control del tiempo.
  • El valor puede ser 0 si no está disponible el control del tiempo para la solicitud actual.

IHttpSysRequestTimingFeature.TryGetTimestamp recupera la marca de tiempo del tipo de control del tiempo proporcionado:

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseHttpSys();

var app = builder.Build();

app.Use((context, next) =>
{
    var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();

    var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger("Sample");

    var timingType = HttpSysRequestTimingType.RequestRoutingEnd;

    if (feature.TryGetTimestamp(timingType, out var timestamp))
    {
        logger.LogInformation("Timestamp {timingType}: {timestamp}",
                                          timingType, timestamp);
    }
    else
    {
        logger.LogInformation("Timestamp {timingType}: not available for the "
                                           + "current request",    timingType);
    }

    return next(context);
});

app.MapGet("/", () => Results.Ok());

app.Run();

Para obtener más información, consulte Implementación del servidor web HTTP.sys en ASP.NET Core y Hospedaje en proceso con IIS y ASP.NET Core.

HTTP.sys: compatibilidad opcional con el almacenamiento en búfer de respuesta en modo kernel

En algunos escenarios, grandes volúmenes de escrituras pequeñas con latencia alta pueden causar un impacto significativo en el rendimiento de HTTP.sys. Este impacto se debe a la falta de un búfer de Pipe en la implementación de HTTP.sys. Para mejorar el rendimiento en estos escenarios, se ha agregado a HTTP.sys compatibilidad con el almacenamiento en búfer de respuesta. Habilite el almacenamiento en búfer estableciendo HttpSysOptions.EnableKernelResponseBuffering en true.

Una aplicación que realiza E/S sincrónica debe habilitar el almacenamiento en búfer de respuesta, o una que realiza E/S asincrónica, pero sin más de una escritura pendiente a la vez. En estos escenarios, el almacenamiento en búfer de respuesta puede mejorar significativamente el rendimiento en las conexiones de alta latencia.

Las aplicaciones que usan E/S asincrónica y que pueden tener más de una escritura pendiente a la vez no deben usar esta marca. Habilitar esta marca puede dar lugar a que HTTP.Sys use más CPU y memoria.

Autenticación y autorización

ASP.NET Core 8 agrega nuevas características a la autenticación y autorización.

Puntos de conexión de API Identity

MapIdentityApi<TUser> es un nuevo método de extensión que agrega dos puntos de conexión de API (/register y /login). El objetivo principal de MapIdentityApi es facilitar que los desarrolladores usen ASP.NET Core Identity para la autenticación en Blazor o aplicaciones de página única (SPA) basadas en JavaScript. En lugar de usar la interfaz de usuario predeterminada proporcionada por ASP.NET Core Identity, que se basa en Razor Pages, MapIdentityApi agrega puntos de conexión de API JSON más adecuados para aplicaciones SPA y aplicaciones que no son de explorador. Para más información, consulte IdentityPuntos de conexión de API.

IAuthorizationRequirementData

Antes de ASP.NET Core 8, para agregar una directiva de autorización parametrizada a un punto de conexión era necesario implementar lo siguiente:

  • AuthorizeAttribute para cada directiva.
  • AuthorizationPolicyProvider para procesar una directiva personalizada a partir de un contrato basado en cadenas.
  • AuthorizationRequirement para la directiva.
  • AuthorizationHandler para cada requisito.

Por ejemplo, considere el ejemplo siguiente escrito para ASP.NET Core 7.0:

using AuthRequirementsData.Authorization;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeAuthorizationHandler>();

var app = builder.Build();

app.MapControllers();

app.Run();
using Microsoft.AspNetCore.Mvc;

namespace AuthRequirementsData.Controllers;

[ApiController]
[Route("api/[controller]")]
public class GreetingsController : Controller
{
    [MinimumAgeAuthorize(16)]
    [HttpGet("hello")]
    public string Hello() => $"Hello {(HttpContext.User.Identity?.Name ?? "world")}!";
}
using Microsoft.AspNetCore.Authorization;
using System.Globalization;
using System.Security.Claims;

namespace AuthRequirementsData.Authorization;

class MinimumAgeAuthorizationHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    private readonly ILogger<MinimumAgeAuthorizationHandler> _logger;

    public MinimumAgeAuthorizationHandler(ILogger<MinimumAgeAuthorizationHandler> logger)
    {
        _logger = logger;
    }

    // Check whether a given MinimumAgeRequirement is satisfied or not for a particular
    // context.
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                               MinimumAgeRequirement requirement)
    {
        // Log as a warning so that it's very clear in sample output which authorization
        // policies(and requirements/handlers) are in use.
        _logger.LogWarning("Evaluating authorization requirement for age >= {age}",
                                                                    requirement.Age);

        // Check the user's age
        var dateOfBirthClaim = context.User.FindFirst(c => c.Type ==
                                                                 ClaimTypes.DateOfBirth);
        if (dateOfBirthClaim != null)
        {
            // If the user has a date of birth claim, check their age
            var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value, CultureInfo.InvariantCulture);
            var age = DateTime.Now.Year - dateOfBirth.Year;
            if (dateOfBirth > DateTime.Now.AddYears(-age))
            {
                // Adjust age if the user hasn't had a birthday yet this year.
                age--;
            }

            // If the user meets the age criterion, mark the authorization requirement
            // succeeded.
            if (age >= requirement.Age)
            {
                _logger.LogInformation("Minimum age authorization requirement {age} satisfied",
                                         requirement.Age);
                context.Succeed(requirement);
            }
            else
            {
                _logger.LogInformation("Current user's DateOfBirth claim ({dateOfBirth})" +
                    " does not satisfy the minimum age authorization requirement {age}",
                    dateOfBirthClaim.Value,
                    requirement.Age);
            }
        }
        else
        {
            _logger.LogInformation("No DateOfBirth claim present");
        }

        return Task.CompletedTask;
    }
}

El código de ejemplo completo se encuentra aquí en el repositorio AspNetCore.Docs.Samples.

ASP.NET Core 8 presenta la interfaz IAuthorizationRequirementData. La interfaz IAuthorizationRequirementData permite que la definición de atributo especifique los requisitos asociados a la directiva de autorización. Con IAuthorizationRequirementData, el código de directiva de autorización personalizada anterior se puede escribir con menos líneas de código. El archivo Program.cs actualizado:

  using AuthRequirementsData.Authorization;
  using Microsoft.AspNetCore.Authorization;
  
  var builder = WebApplication.CreateBuilder();
  
  builder.Services.AddAuthentication().AddJwtBearer();
  builder.Services.AddAuthorization();
  builder.Services.AddControllers();
- builder.Services.AddSingleton<IAuthorizationPolicyProvider, MinimumAgePolicyProvider>();
  builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeAuthorizationHandler>();
  
  var app = builder.Build();
  
  app.MapControllers();
  
  app.Run();

MinimumAgeAuthorizationHandler actualizado:

using Microsoft.AspNetCore.Authorization;
using System.Globalization;
using System.Security.Claims;

namespace AuthRequirementsData.Authorization;

- class MinimumAgeAuthorizationHandler : AuthorizationHandler<MinimumAgeRequirement>
+ class MinimumAgeAuthorizationHandler : AuthorizationHandler<MinimumAgeAuthorizeAttribute>
{
    private readonly ILogger<MinimumAgeAuthorizationHandler> _logger;

    public MinimumAgeAuthorizationHandler(ILogger<MinimumAgeAuthorizationHandler> logger)
    {
        _logger = logger;
    }

    // Check whether a given MinimumAgeRequirement is satisfied or not for a particular
    // context
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
-                                              MinimumAgeRequirement requirement)
+                                              MinimumAgeAuthorizeAttribute requirement)
    {
        // Remaining code omitted for brevity.

Puede encontrar el ejemplo actualizado completo aquí.

Consulte Directivas de autorización personalizadas con IAuthorizationRequirementData para obtener un examen detallado del nuevo ejemplo.

Protección de puntos de conexión de interfaz de usuario de Swagger

Los puntos de conexión de la interfaz de usuario de Swagger ahora se pueden proteger en entornos de producción llamando a MapSwagger().RequireAuthorization. Para obtener más información, consulte Protección de puntos de conexión de la interfaz de usuario de Swagger.

Disposiciones adicionales

En las secciones siguientes se describen varias características nuevas en ASP.NET Core 8.

Compatibilidad de servicios con claves en la inserción de dependencias

Los servicios con claves hacen referencia a un mecanismo para registrar y recuperar servicios de inserción de dependencias (DI) mediante claves. Un servicio se asocia a una clave llamando AddKeyedSingleton a (o AddKeyedScoped o AddKeyedTransient) para registrarlo. Acceda a un servicio registrado especificando la clave con el atributo [FromKeyedServices]. En el código siguiente se muestra cómo usar servicios con claves:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddControllers();

var app = builder.Build();

app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) =>
                                                               smallCache.Get("date"));

app.MapControllers();

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

[ApiController]
[Route("/cache")]
public class CustomServicesApiController : Controller
{
    [HttpGet("big-cache")]
    public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache)
    {
        return cache.Get("data-mvc");
    }
}

public class MyHub : Hub
{
    public void Method([FromKeyedServices("small")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }
}

Plantillas de proyecto de Visual Studio para aplicaciones SPA con back-end de ASP.NET Core

Las plantillas de proyecto de Visual Studio son ahora la manera recomendada de crear aplicaciones de página única (SPA) que tengan un back-end de ASP.NET Core. Se proporcionan plantillas que crean aplicaciones basadas en los marcos de JavaScript Angular, React y Vue. Estas plantillas:

  • Crean una solución de Visual Studio con un proyecto de front-end y un proyecto de back-end.
  • Usan el tipo de proyecto de Visual Studio para JavaScript y TypeScript (.esproj) para el front-end.
  • Usan un proyecto de ASP.NET Core para el back-end.

Para obtener más información sobre las plantillas de Visual Studio y cómo acceder a las plantillas heredadas, consulte Información general sobre aplicaciones de página única (SPA) en ASP.NET Core

Compatibilidad con atributos genéricos

Los atributos que antes requerían un parámetro Type ahora están disponibles en variantes genéricas más limpias. Esto es posible gracias a la compatibilidad con atributos genéricos en C# 11. Por ejemplo, la sintaxis para anotar el tipo de respuesta de una acción se puede modificar de la siguiente manera:

[ApiController]
[Route("api/[controller]")]
public class TodosController : Controller
{
  [HttpGet("/")]
- [ProducesResponseType(typeof(Todo), StatusCodes.Status200OK)]
+ [ProducesResponseType<Todo>(StatusCodes.Status200OK)]
  public Todo Get() => new Todo(1, "Write a sample", DateTime.Now, false);
}

Se admiten variantes genéricas para los siguientes atributos:

  • [ProducesResponseType<T>]
  • [Produces<T>]
  • [MiddlewareFilter<T>]
  • [ModelBinder<T>]
  • [ModelMetadataType<T>]
  • [ServiceFilter<T>]
  • [TypeFilter<T>]

Análisis de código en aplicaciones de ASP.NET Core

Los nuevos analizadores que se muestran en la tabla siguiente están disponibles en ASP.NET Core 8.0.

Id. de diagnóstico Importante o no importante Descripción
ASP0016 No importante No devolver un valor de RequestDelegate
ASP0019 No importante Sugerir el uso de IHeaderDictionary.Append o del indexador
ASP0020 No importante Los tipos complejos a los que hacen referencia los parámetros de ruta deben poder analizarse
ASP0021 No importante El tipo de valor devuelto del método BindAsync debe ser ValueTask<T>
ASP0022 No importante Conflicto de ruta detectado entre controladores de ruta
ASP0023 No importante MVC: conflicto de ruta detectado entre controladores de ruta
ASP0024 No importante El controlador de ruta tiene varios parámetros con el atributo [FromBody]
ASP0025 No importante Uso de AddAuthorizationBuilder

Herramientas de enrutamiento

ASP.NET Core se basa en el enrutamiento. Las API mínimas, las API web, las páginas de Razor y Blazor usan rutas para personalizar la forma en la que se asignan las solicitudes HTTP al código.

En .NET 8 hemos invertido en un conjunto de características nuevas para facilitar el enrutamiento y su uso. Estas características nuevas incluyen:

Para obtener más información, consulte Herramientas de enrutamiento en .NET 8.

Métricas de ASP.NET Core

Las métricas son medidas numéricas notificadas con el tiempo que se suelen usar para supervisar el estado de una aplicación y generar alertas. Por ejemplo, un contador que informa de solicitudes HTTP erróneas podría mostrarse en paneles o generar alertas cuando los errores superan un umbral.

Esta versión preliminar agrega nuevas métricas a lo largo de ASP.NET Core mediante System.Diagnostics.Metrics. Metrics es una API moderna para generar informes y recopilar información sobre las aplicaciones.

Las métricas ofrecen varias mejoras en comparación con los contadores de eventos existentes:

  • Nuevos tipos de medidas con contadores, medidores y histogramas.
  • Informes eficaces con valores multidimensionales.
  • Integración en el ecosistema nativo de nube más amplio mediante la alineación con los estándares de OpenTelemetry.

Se han agregado métricas para ASP.NET Core hospedaje, Kestrel y SignalR. Para más información, consulte System.Diagnostics.Metrics.

IExceptionHandler

IExceptionHandler es una nueva interfaz que proporciona al desarrollador una devolución de llamada para controlar excepciones conocidas en una ubicación central.

Las implementaciones de IExceptionHandler se registran mediante una llamada de IServiceCollection.AddExceptionHandler<T>. Se pueden agregar varias implementaciones y se llaman en el orden registrado. Si un controlador de excepciones controla una solicitud, puede devolver true para detener el procesamiento. Si un controlador de excepciones no controla una excepción, el control vuelve al comportamiento predeterminado y a las opciones del middleware.

Para obtener más información, vea IExceptionHandler.

Experiencia de depuración mejorada

Los atributos de personalización de depuración se han agregado a tipos como HttpContext, HttpRequest, HttpResponse, ClaimsPrincipal y WebApplication. Las pantallas del depurador mejorado para estos tipos facilitan la búsqueda de información importante en el depurador de un IDE. En las capturas de pantalla siguientes se muestra la diferencia que estos atributos hacen en la presentación del depurador de HttpContext.

.NET 7:

Visualización poco útil del depurador del tipo HttpContext en .NET 7.

.NET 8:

Visualización útil del depurador del tipo HttpContext en .NET 8.

La pantalla del depurador para WebApplication resalta información importante, como puntos de conexión configurados, middleware y valores de IConfiguration.

.NET 7:

Visualización poco útil del depurador del tipo WebApplication en .NET 7.

.NET 8:

Visualización útil del depurador del tipo WebApplication en .NET 8.

Para obtener más información sobre las mejoras de depuración en .NET 8, consulte:

IPNetwork.Parse y TryParse

Los nuevos métodos Parse y TryParse en IPNetwork agregan compatibilidad para crear una IPNetwork mediante una cadena de entrada en notación CIDR o "notación de barra diagonal".

Estos son ejemplos de IPv4:

// Using Parse
var network = IPNetwork.Parse("192.168.0.1/32");
// Using TryParse
bool success = IPNetwork.TryParse("192.168.0.1/32", out var network);
// Constructor equivalent
var network = new IPNetwork(IPAddress.Parse("192.168.0.1"), 32);

Y estos son ejemplos de IPv6:

// Using Parse
var network = IPNetwork.Parse("2001:db8:3c4d::1/128");
// Using TryParse
bool success = IPNetwork.TryParse("2001:db8:3c4d::1/128", out var network);
// Constructor equivalent
var network = new IPNetwork(IPAddress.Parse("2001:db8:3c4d::1"), 128);

Almacenamiento en caché de salida basado en Redis

ASP.NET Core 8 agrega compatibilidad con el uso de Redis como caché distribuida para el almacenamiento en caché de salida. El almacenamiento en caché de la salida es una característica que permite a una aplicación almacenar en caché la salida de un punto de conexión mínimo de la API, una acción del controlador o una Razor página. Para más información, vea Almacenamiento en caché de salida.

Cortocircuito de middleware después del enrutamiento

Cuando el enrutamiento coincide con un punto de conexión, normalmente permite que el rest de la canalización de middleware se ejecute antes de invocar la lógica del punto de conexión. Los servicios pueden reducir el uso de recursos filtrando las solicitudes conocidas al principio de la canalización. Use el método de extensión ShortCircuit para hacer que el enrutamiento invoque la lógica del punto de conexión inmediatamente y, a continuación, finalice la solicitud. Por ejemplo, es posible que una ruta determinada no tenga que pasar por la autenticación o el middleware CORS. En el ejemplo siguiente se cortocircuitan las solicitudes que coinciden con la ruta /short-circuit:

app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();

Use el método MapShortCircuit para configurar el cortocircuito para varias rutas a la vez, pasando a ella una matriz de parámetros de prefijos de dirección URL. Por ejemplo, los exploradores y los bots suelen sondear servidores para rutas de acceso conocidas como robots.txt y favicon.ico. Si la aplicación no tiene esos archivos, una línea de código puede configurar ambas rutas:

app.MapShortCircuit(404, "robots.txt", "favicon.ico");

Para obtener más información, vea Middleware de cortocircuito después del enrutamiento.

Extensibilidad del middleware de registro HTTP

El middleware de registro HTTP tiene varias funcionalidades nuevas:

  • HttpLoggingFields.Duration: cuando está habilitado, el middleware emite un nuevo registro al final de la solicitud y una respuesta que mide el tiempo total necesario para su procesamiento. Este nuevo campo se ha agregado al conjunto HttpLoggingFields.All.
  • HttpLoggingOptions.CombineLogs: cuando está habilitado, el middleware consolida todos sus registros habilitados para una solicitud y una respuesta en un registro al final. Un único mensaje de registro incluye la solicitud, el cuerpo de la solicitud, la respuesta, el cuerpo de la respuesta y la duración.
  • IHttpLoggingInterceptor: una nueva interfaz para un servicio que se puede implementar y registrar (mediante AddHttpLoggingInterceptor) para recibir devoluciones de llamada por solicitud y por respuesta para personalizar los detalles que se registran. Cualquier configuración de registro específica del punto de conexión se aplica primero y, a continuación, se puede invalidar en estas devoluciones de llamada. Una implementación puede:
    • Inspeccionar una solicitud y una respuesta.
    • Habilite o deshabilite cualquier HttpLoggingFields.
    • Ajuste la cantidad del cuerpo de la solicitud o respuesta que se registra.
    • Agregue campos personalizados a los registros.

Para obtener más información, consulte Registro HTTP en .NET Core y ASP.NET Core.

Nuevas API en ProblemDetails para admitir integraciones más resistentes

En .NET 7, el Servicio ProblemDetails se introdujo para mejorar la experiencia para generar respuestas de error que cumplan la Especificación ProblemDetails. En .NET 8, se ha agregado una nueva API para facilitar la implementación del comportamiento de reserva si IProblemDetailsService no puede generar ProblemDetails. En el ejemplo siguiente se muestra el uso de la nueva TryWriteAsync API:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/exception", () =>
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Para obtener más información, consulte Fallback de IProblemDetailsService

Recursos adicionales