Novedades de ASP.NET Core 6.0

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

Mejoras en ASP.NET Core MVC y Razor

API mínimas

Las API mínimas están diseñadas para crear API HTTP con dependencias mínimas. Son ideales para microservicios y aplicaciones que desean incluir solo los archivos, las características y las dependencias mínimas en ASP.NET Core. Para más información, consulte:

SignalR

Etiqueta de actividad de larga duración para conexiones SignalR

SignalR usa la nueva Microsoft.AspNetCore.Http.Features.IHttpActivityFeature.Activity para agregar una etiqueta http.long_running a la actividad de solicitud. Los servicios de APM, como Application Insights de Azure Monitor, usan IHttpActivityFeature.Activity para filtrar las solicitudes de SignalR de la creación de alertas de solicitud de larga duración.

Mejoras en el rendimiento de SignalR

Compilador de Razor

El compilador de Razor se actualizó para usar generadores de código fuente

El compilador de Razor ahora se basa en generadores de código fuente de C#. Los generadores de código fuente se ejecutan durante la compilación e inspeccionan lo que se compila a fin de generar archivos adicionales que se compilan junto con el resto del proyecto. El uso de generadores de código fuente simplifica el compilador de Razor y acelera considerablemente los tiempos de compilación.

El compilador de Razor ya no genera un ensamblado de Vistas independiente

Anteriormente, el compilador de Razor utilizaba un proceso de compilación de dos pasos que generaba un ensamblado de Vistas independiente que contenía las vistas y páginas generadas (archivos .cshtml) definidas en la aplicación. Los tipos generados eran públicos y estaban en el espacio de nombres AspNetCore.

El compilador de Razor actualizado genera los tipos de vistas y páginas en el ensamblado principal del proyecto. Estos tipos ahora se generan de manera predeterminada como sellados internamente en el espacio de nombres AspNetCoreGeneratedDocument. Este cambio mejora el rendimiento de la compilación, activa la implementación de un archivo único y permite que estos tipos participen en la Recarga activa.

Para más información sobre este cambio, consulte el problema de anuncio relacionado en GitHub.

Mejoras en las API y el rendimiento de ASP.NET Core

Se hicieron muchos cambios para reducir las asignaciones y mejorar el rendimiento en toda la pila:

Se redujo la superficie de memoria para conexiones TLS inactivas

Para las conexiones TLS de larga duración en las que los datos solo se envían ocasionalmente de un lado a otro, redujimos considerablemente la superficie de memoria de las aplicaciones ASP.NET Core en .NET 6. Esto debería permitir mejorar la escalabilidad de escenarios como los servidores WebSocket. Esto fue posible debido a numerosas mejoras en System.IO.Pipelines, SslStream y Kestrel. En las secciones siguientes se detallan algunas de las mejoras que contribuyeron a reducir la superficie de memoria:

Reducción del tamaño de System.IO.Pipelines.Pipe

Para cada conexión establecida, se asignan dos canalizaciones en Kestrel:

  • La capa de transporte a la capa de aplicación para la solicitud.
  • La capa de aplicación a la capa de transporte para la respuesta.

Al disminuir el tamaño de System.IO.Pipelines.Pipe de 368 bytes a 264 bytes (una reducción aproximada del 28,2 %), se ahorran 208 bytes por conexión (104 bytes por canalización).

Agrupación de SocketSender

Los objetos SocketSender (de la subclase SocketAsyncEventArgs) tienen unos 350 bytes en tiempo de ejecución. En lugar de asignar un objeto SocketSender nuevo por conexión, se pueden agrupar. Los objetos SocketSender se pueden agrupar porque los envíos suelen ser muy rápidos. Agruparlos reduce la sobrecarga por conexión. En lugar de asignar 350 bytes por conexión, solo se asignan 350 bytes por IOQueue. Para evitar la contención, la asignación se hace por cola. Nuestro servidor WebSocket con 5000 conexiones inactivas pasó de asignar alrededor de 1,75 MB (350 bytes * 5000) a asignar alrededor de 2,8 KB (350 bytes * 8) para los objetos SocketSender.

Lecturas de cero bytes con SslStream

Las lecturas sin búfer son una técnica que se emplea en ASP.NET Core para evitar alquilar memoria del grupo de memoria si no hay datos disponibles en el socket. Antes de esta modificación, el servidor WebSocket con 5000 conexiones inactivas requería alrededor de 200 MB sin TLS, en comparación con alrededor de 800 MB con TLS. Algunas de estas asignaciones (4000 por conexión) provenían de que Kestrel tenía que mantener un búfer ArrayPool<T> mientras se esperaba que se completaran las lecturas en SslStream. Dado que estas conexiones estaban inactivas, ninguna de las lecturas se completó ni volvió su búfer a ArrayPool, lo que obligaba a ArrayPool a asignar más memoria. Las asignaciones restantes estaban en el SslStream mismo: el búfer de 4000 para protocolos de enlace TLS y el búfer de 32 000 para lecturas normales. En .NET 6, cuando el usuario realiza una lectura de cero bytes en SslStream y no tiene datos disponibles, SslStream realiza una lectura de cero bytes de manera interna en la secuencia ajustada subyacente. En el mejor de los casos (conexión inactiva), estos cambios generan un ahorro de 40 KB por conexión, a la vez que permiten que el usuario (Kestrel) reciba una notificación cuando los datos estén disponibles in mantener ningún búfer no utilizado.

Lecturas de cero bytes con PipeReader

Como SslStream admitía las lecturas sin búfer, se agregó una opción para realizar lecturas de cero bytes a StreamPipeReader, el tipo interno que adapta un Stream en un PipeReader. En Kestrel, se usa un StreamPipeReader para adaptar el SslStream subyacente a un PipeReader. Por lo tanto, era necesario exponer esta semántica de la lectura de cero bytes en PipeReader.

Ahora se puede crear un PipeReader que admita lecturas de cero bytes en cualquier Stream subyacente que admita la semántica de lectura de cero bytes (por ejemplo, SslStream, NetworkStream, etc) con la API siguiente:

var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(useZeroByteReads: true));

Eliminación de bloques de SlabMemoryPool

A fin de reducir la fragmentación del montón, Kestrel utilizó una técnica en la que asignaba bloques de memoria de 128 KB como parte de su grupo de memoria. Luego, los bloques se dividían en bloques de 4 KB que Kestrel usaba de manera interna. Los bloques debían tener más de 85 KB para forzar la asignación en el montón de objetos de gran tamaño para intentar impedir que el GC reasignara esta matriz. Sin embargo, con la introducción de la generación nueva de GC, Montón de objetos anclados (POH), ya no tiene sentido asignar bloques secundarios en el bloque mayor. Kestrel ahora asigna directamente bloques en el Montón de objetos anclados, lo que disminuye la complejidad que implica la administración del grupo de memoria. Este cambio debería facilitar las mejoras futuras, como la reducción del grupo de memoria que Kestrel usa.

IAsyncDisposable compatible

IAsyncDisposable ahora está disponible para controladores, Razor Pages y componentes de vista. Se agregaron versiones asincrónicas a las interfaces pertinentes en fábricas y activadores:

  • Los métodos nuevos ofrecen una implementación de interfaz predeterminada que delega a la versión sincrónica y llama a Dispose.
  • Las implementaciones reemplazan la implementación predeterminada y controlan la eliminación de las implementaciones de IAsyncDisposable.
  • Las implementaciones favorecen a IAsyncDisposable por sobre IDisposable cuando se implementan ambas interfaces.
  • Los controles extensores deben invalidar los métodos nuevos que se incluyen para admitir instancias de IAsyncDisposable.

IAsyncDisposable es beneficioso al trabajar con:

  • Enumeradores asincrónicos; por ejemplo, en secuencias asincrónicas.
  • Recursos no administrados que tienen operaciones de E/S que consumen muchos recursos que se van a liberar.

Al implementar esta interfaz, utilice el método DisposeAsync para liberar recursos.

Imagine un controlador que crea y utiliza un Utf8JsonWriter. Utf8JsonWriter es un recurso IAsyncDisposable:

public class HomeController : Controller, IAsyncDisposable
{
    private Utf8JsonWriter? _jsonWriter;
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
        _jsonWriter = new Utf8JsonWriter(new MemoryStream());
    }

IAsyncDisposable debe implementar DisposeAsync:

public async ValueTask DisposeAsync()
{
    if (_jsonWriter is not null)
    {
        await _jsonWriter.DisposeAsync();
    }

    _jsonWriter = null;
}

Puerto vcpkg para el cliente de C++ de SignalR

vcpkg es un administrador de paquetes de la línea de comandos multiplataforma para bibliotecas de C y C++. Recientemente, agregamos un puerto a vcpkg para agregar compatibilidad nativa de CMake con el cliente de C++ de SignalR. vcpkg también funciona con MSBuild.

El cliente de SignalR se puede agregar a un proyecto de CMake con el fragmento siguiente cuando el vcpkg esté incluido en el archivo de la cadena de herramientas:

find_package(microsoft-signalr CONFIG REQUIRED)
link_libraries(microsoft-signalr::microsoft-signalr)

Con el fragmento anterior, el cliente de C++ de SignalR está listo para utilizar #include y se puede usar en un proyecto sin necesidad de configuración adicional. Para obtener un ejemplo completo de una aplicación de C++ que usa el cliente de C++ de SignalR, vea el repositorio halter73/SignalR-Client-Cpp-Sample.

Blazor

Cambios en la plantilla de proyecto

Se hicieron varios cambios en la plantilla de proyecto para las aplicaciones Blazor, incluido el uso del archivo Pages/_Layout.cshtml para el contenido del diseño que aparecía en el archivo _Host.cshtml para las aplicaciones Blazor Server anteriores. Para estudiar los cambios, cree una aplicación a partir de una plantilla de proyecto 6.0 o acceda al origen de referencia de ASP.NET Core para las plantillas de proyecto:

Compatibilidad con dependencias nativas de Blazor WebAssembly

Las aplicaciones Blazor WebAssembly pueden usar dependencias nativas compiladas para ejecutarse en WebAssembly. Para más información, vea Dependencias nativas de Blazor WebAssembly en ASP.NET Core.

Compilación de WebAssembly ahead-of-time (AOT) y revinculación en tiempo de ejecución

Blazor WebAssembly admite la compilación Ahead Of Time(AOT), donde puede compilar el código de .NET directamente en WebAssembly. La compilación AOT da como resultado mejoras de rendimiento en tiempo de ejecución a costa de un tamaño de aplicación mayor. Al volver a vincular el entorno de ejecución de WebAssembly de .NET, se recorta el código en tiempo de ejecución sin usar y, por tanto, se mejora la velocidad de descarga. Para más información, consulte Compilación ahead-of-time (AOT) y Revinculación en tiempo de ejecución.

Conservación del estado previamente representado

Blazor admite el estado persistente en una página previamente representada para que no sea necesario volver a crear el estado cuando la aplicación esté totalmente cargada. Para más información, vea Integración y representación previa de componentes Razor de ASP.NET Core.

Límites de error

Los límites de error proporcionan un enfoque práctico para controlar las excepciones en el nivel de interfaz de usuario. Para más información, vea Control de errores en aplicaciones Blazor de ASP.NET Core.

Compatibilidad con SVG

El elemento <foreignObject> se admite para mostrar HTML arbitrario dentro de un archivo SVG. Para más información, vea Componentes Razor de ASP.NET Core.

Compatibilidad de Blazor Server con la transferencia de matriz de bytes en interoperabilidad de JS

Blazor admite la interoperabilidad de JS de la matriz de bytes optimizada que evita la codificación y decodificación de matrices de bytes en Base64. Para obtener más información, consulte los siguientes recursos:

Mejoras en la cadena de consulta

Se mejoró la compatibilidad para trabajar con cadenas de consulta. Para más información, vea Enrutamiento y navegación de Blazor de ASP.NET Core.

Enlace para seleccionar varios elementos

El enlace admite la selección de varias opciones con elementos <input>. Para obtener más información, consulte los siguientes recursos:

Control de contenido de encabezado (<head>)

Los componentes Razor pueden modificar el contenido del elemento HTML <head> de una página, incluido el establecimiento del título de la página (elemento <title>) y la modificación de metadatos (elementos <meta>). Para más información, vea Control del contenido de <head> en aplicaciones Blazor de ASP.NET Core.

Generación de componentes Angular y React

Genere componentes de JavaScript específicos del marco de trabajo a partir de componentes Razor para marcos web, como Angular o React. Para más información, vea Componentes Razor de ASP.NET Core.

Representación de componentes desde JavaScript

Represente los componentes de Razor de manera dinámica desde JavaScript para aplicaciones de JavaScript existentes. Para más información, vea Componentes Razor de ASP.NET Core.

Elementos personalizados

La compatibilidad experimental está disponible para crear elementos personalizados que utilizan interfaces HTML estándar. Para más información, vea Componentes Razor de ASP.NET Core.

Inferencia de tipos genéricos de componentes a partir de componentes antecesores

Un componente antecesor puede organizar en cascada un parámetro de tipo por nombre hasta los descendientes mediante el atributo [CascadingTypeParameter] nuevo. Para más información, vea Componentes Razor de ASP.NET Core.

Componentes representados dinámicamente

Use el componente integrado DynamicComponent nuevo para representar los componentes por tipo. Para más información, vea Componentes Razor de ASP.NET Core representados dinámicamente.

Accesibilidad a Blazor mejorada

Use el componente FocusOnNavigate nuevo para establecer el foco de la interfaz de usuario en un elemento basado en un selector CSS después de navegar de una página a otra. Para más información, vea Enrutamiento y navegación de Blazor de ASP.NET Core.

Compatibilidad con argumentos de eventos personalizados

Blazor admite argumentos de eventos personalizados, que permiten pasar datos arbitrarios a controladores de eventos de .NET con eventos personalizados. Para más información, vea Control de eventos de Blazor en ASP.NET Core.

Parámetros obligatorios

Aplique el atributo [EditorRequired] nuevo para especificar un parámetro de componente necesario. Para más información, vea Componentes Razor de ASP.NET Core.

Colocación de archivos de JavaScript con páginas, vistas y componentes

La colocación de archivos de JavaScript para páginas, vistas y componentes de Razor es una manera cómoda de organizar scripts en una aplicación. Para más información, vea Interoperabilidad de JavaScript en Blazor de ASP.NET Core (interoperabilidad de JS).

Inicializadores de JavaScript

Los inicializadores de JavaScript ejecutan lógica antes y después de que se carga una aplicación Blazor. Para más información, vea Interoperabilidad de JavaScript en Blazor de ASP.NET Core (interoperabilidad de JS).

Streaming de interoperabilidad de JavaScript

Blazor ahora admite el streaming de datos directamente entre .NET y JavaScript. Para obtener más información, consulte los siguientes recursos:

Restricciones de tipo genérico

Ahora se admiten parámetros de tipo genérico. Para más información, vea Componentes Razor de ASP.NET Core.

Diseño de implementación de WebAssembly

Use un diseño de implementación para habilitar las descargas de aplicaciones Blazor WebAssembly en entornos de seguridad restringida. Para más información, vea Diseño de implementación para aplicaciones Blazor WebAssembly hospedadas en ASP.NET Core.

Artículos nuevos sobre Blazor

Además de las características de Blazor descritas en las secciones anteriores, hay artículos nuevos sobre Blazor disponibles acerca de los temas siguientes:

Creación de aplicaciones Blazor Hybrid con .NET MAUI, WPF y Windows Forms

Use Blazor Hybrid para combinar marcos de cliente nativos móviles y de escritorio con .NET y Blazor:

  • .NET Multi-platform App UI (.NET MAUI) es un marco multiplataforma para crear aplicaciones móviles y de escritorio nativas con C# y XAML.
  • Las aplicaciones Blazor Hybrid se pueden crear con Windows Presentation Foundation (WPF) y Windows Forms.

Importante

Blazor Hybrid está en versión preliminar y no se debe usar en aplicaciones de producción hasta la versión final.

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

Kestrel

Actualmente, HTTP/3 está en modo borrador y, por lo tanto, está sujeto a cambios. Todavía no se publica la compatibilidad con HTTP/3 en ASP.NET Core, sino que es una característica en vista previa (GB) incluida en .NET 6.

Kestrel ahora admite HTTP/3. Para más información, vea Uso de HTTP/3 con el servidor web Kestrel de ASP.NET Core y la entrada de blog Compatibilidad con HTTP/3 in .NET 6.

Categorías nuevas de registro de Kestrel para el registro seleccionado

Antes de este cambio, habilitar el registro detallado para Kestrel era demasiado costoso, porque todas las instancias de Kestrel compartían el nombre de la categoría de registro Microsoft.AspNetCore.Server.Kestrel. Microsoft.AspNetCore.Server.Kestrel sigue disponible, pero las subcategorías nuevas siguientes permiten un mayor control del registro:

  • Microsoft.AspNetCore.Server.Kestrel (categoría actual): ApplicationError, ConnectionHeadResponseBodyWrite, ApplicationNeverCompleted, RequestBodyStart, RequestBodyDone, RequestBodyNotEntirelyRead, RequestBodyDrainTimedOut, ResponseMinimumDataRateNotSatisfied, InvalidResponseHeaderRemoved, HeartbeatSlow.
  • Microsoft.AspNetCore.Server.Kestrel.BadRequests: ConnectionBadRequest, RequestProcessingError, RequestBodyMinimumDataRateNotSatisfied.
  • Microsoft.AspNetCore.Server.Kestrel.Connections: ConnectionAccepted, ConnectionStart, ConnectionStop, ConnectionPause, ConnectionResume, ConnectionKeepAlive, ConnectionRejected, ConnectionDisconnect, NotAllConnectionsClosedGracefully, NotAllConnectionsAborted, ApplicationAbortedConnection.
  • Microsoft.AspNetCore.Server.Kestrel.Http2: Http2ConnectionError, Http2ConnectionClosing, Http2ConnectionClosed, Http2StreamError, Http2StreamResetAbort, HPackDecodingError, HPackEncodingError, Http2FrameReceived, Http2FrameSending, Http2MaxConcurrentStreamsReached.
  • Microsoft.AspNetCore.Server.Kestrel.Http3: Http3ConnectionError, Http3ConnectionClosing, Http3ConnectionClosed, Http3StreamAbort, Http3FrameReceived, Http3FrameSending.

Las reglas existentes siguen funcionando, pero ahora puede ser más selectivo respecto de las reglas que habilita. Por ejemplo, la sobrecarga de observabilidad de habilitar el registro de Debug solo para solicitudes incorrectas se reduce considerablemente y se puede habilitar con la configuración siguiente:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.Kestrel.BadRequests": "Debug"
    }
  }

El filtrado de registros aplica reglas con el prefijo de categoría coincidente más largo. Para más información, consulte Cómo se aplican las reglas de filtro.

Emisión de KestrelServerOptions a través del evento EventSource

KestrelEventSource emite un evento nuevo que contiene el objeto JS serializado por KestrelServerOptionsON cuando se habilita con el nivel de detalle EventLevel.LogAlways. Este evento facilita razonar sobre el comportamiento del servidor al analizar los seguimientos recopilados. El JSON siguiente es un ejemplo de la carga del evento:

{
  "AllowSynchronousIO": false,
  "AddServerHeader": true,
  "AllowAlternateSchemes": false,
  "AllowResponseHeaderCompression": true,
  "EnableAltSvc": false,
  "IsDevCertLoaded": true,
  "RequestHeaderEncodingSelector": "default",
  "ResponseHeaderEncodingSelector": "default",
  "Limits": {
    "KeepAliveTimeout": "00:02:10",
    "MaxConcurrentConnections": null,
    "MaxConcurrentUpgradedConnections": null,
    "MaxRequestBodySize": 30000000,
    "MaxRequestBufferSize": 1048576,
    "MaxRequestHeaderCount": 100,
    "MaxRequestHeadersTotalSize": 32768,
    "MaxRequestLineSize": 8192,
    "MaxResponseBufferSize": 65536,
    "MinRequestBodyDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
    "MinResponseDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
    "RequestHeadersTimeout": "00:00:30",
    "Http2": {
      "MaxStreamsPerConnection": 100,
      "HeaderTableSize": 4096,
      "MaxFrameSize": 16384,
      "MaxRequestHeaderFieldSize": 16384,
      "InitialConnectionWindowSize": 131072,
      "InitialStreamWindowSize": 98304,
      "KeepAlivePingDelay": "10675199.02:48:05.4775807",
      "KeepAlivePingTimeout": "00:00:20"
    },
    "Http3": {
      "HeaderTableSize": 0,
      "MaxRequestHeaderFieldSize": 16384
    }
  },
  "ListenOptions": [
    {
      "Address": "https://127.0.0.1:7030",
      "IsTls": true,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "https://[::1]:7030",
      "IsTls": true,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "http://127.0.0.1:5030",
      "IsTls": false,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "http://[::1]:5030",
      "IsTls": false,
      "Protocols": "Http1AndHttp2"
    }
  ]
}

Evento DiagnosticSource nuevo para solicitudes HTTP rechazadas

Kestrel ahora emite un evento DiagnosticSource nuevo para solicitudes HTTP rechazadas en la capa de servidor. Antes de este cambio, no había manera de observar estas solicitudes rechazadas. El evento Microsoft.AspNetCore.Server.Kestrel.BadRequest nuevo de DiagnosticSource contiene un objeto IBadRequestExceptionFeature que se puede usar para hacer una introspección del motivo para rechazar la solicitud.

using Microsoft.AspNetCore.Http.Features;
using System.Diagnostics;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var diagnosticSource = app.Services.GetRequiredService<DiagnosticListener>();
using var badRequestListener = new BadRequestEventListener(diagnosticSource,
    (badRequestExceptionFeature) =>
{
    app.Logger.LogError(badRequestExceptionFeature.Error, "Bad request received");
});
app.MapGet("/", () => "Hello world");

app.Run();

class BadRequestEventListener : IObserver<KeyValuePair<string, object>>, IDisposable
{
    private readonly IDisposable _subscription;
    private readonly Action<IBadRequestExceptionFeature> _callback;

    public BadRequestEventListener(DiagnosticListener diagnosticListener,
                                   Action<IBadRequestExceptionFeature> callback)
    {
        _subscription = diagnosticListener.Subscribe(this!, IsEnabled);
        _callback = callback;
    }
    private static readonly Predicate<string> IsEnabled = (provider) => provider switch
    {
        "Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true,
        _ => false
    };
    public void OnNext(KeyValuePair<string, object> pair)
    {
        if (pair.Value is IFeatureCollection featureCollection)
        {
            var badRequestFeature = featureCollection.Get<IBadRequestExceptionFeature>();

            if (badRequestFeature is not null)
            {
                _callback(badRequestFeature);
            }
        }
    }
    public void OnError(Exception error) { }
    public void OnCompleted() { }
    public virtual void Dispose() => _subscription.Dispose();
}

Para más información, vea Registro y diagnóstico en Kestrel.

Creación de un objeto ConnectionContext a partir de un socket de aceptación

El objeto SocketConnectionContextFactory nuevo permite crear un objeto ConnectionContext a partir de un socket aceptado. De este modo, es posible crear un objeto IConnectionListenerFactory basado en socket personalizado sin perder todo el trabajo de rendimiento y la agrupación que ocurre en SocketConnection.

Consulte este ejemplo de un objeto IConnectionListenerFactory personalizado que muestra cómo usar esta objeto SocketConnectionContextFactory.

Kestrel es el perfil de inicio predeterminado para Visual Studio

El perfil de inicio predeterminado para todos los proyectos web dotnet nuevos es Kestrel. El inicio de Kestrel es considerablemente más rápido y genera una experiencia con mayor capacidad de respuesta al desarrollar aplicaciones.

IIS Express sigue disponible como perfil de inicio para escenarios como la autenticación de Windows o el uso compartido de puertos.

Los puertos localhost para Kestrel son aleatorios

Vea Puertos generados por plantillas para Kestrel en este documento para más información.

Autenticación y autorización

Servidores de autenticación

De .NET 3 a .NET 5 se usaba IdentityServer4 como parte de la plantilla para permitir la emisión de tokens JWT para SPA y aplicaciones Blazor. Ahora en las plantillas se usa Duende Identity Server.

Si va a extender los modelos de identidad y actualizar proyectos existentes, debe actualizar los espacios de nombres del código de IdentityServer4.IdentityServer a Duende.IdentityServer y seguir las instrucciones de migración.

El modelo de licencia de Duende Identity Server cambió a una licencia recíproca que puede requerir tarifas de licencia si se usa de manera comercial en un entorno de producción. Para más detalles, consulte la página de licencias de Duende.

Negociación retrasada de certificados de cliente

Los desarrolladores ahora pueden optar por usar la negociación retrasada de certificados de cliente. Para ello, deben especificarClientCertificateMode.DelayCertificate en HttpsConnectionAdapterOptions. Esto solo funciona con conexiones HTTP/1.1, porque HTTP/2 prohíbe la renegociación retrasada de certificados. El autor de la llamada de esta API debe almacenar en búfer el cuerpo de la solicitud antes de solicitar el certificado de cliente:

using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.WebUtilities;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(options =>
{
    options.ConfigureHttpsDefaults(adapterOptions =>
    {
        adapterOptions.ClientCertificateMode = ClientCertificateMode.DelayCertificate;
    });
});

var app = builder.Build();
app.Use(async (context, next) =>
{
    bool desiredState = GetDesiredState();
    // Check if your desired criteria is met
    if (desiredState)
    {
        // Buffer the request body
        context.Request.EnableBuffering();
        var body = context.Request.Body;
        await body.DrainAsync(context.RequestAborted);
        body.Position = 0;

        // Request client certificate
        var cert = await context.Connection.GetClientCertificateAsync();

        //  Disable buffering on future requests if the client doesn't provide a cert
    }
    await next(context);
});


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

La expiración deslizante de la autenticación de Cookie ahora se puede personalizar o suprimir mediante el nuevo objeto OnCheckSlidingExpiration. Por ejemplo, este evento lo puede usar una aplicación de página única que necesita hacer ping periódicamente al servidor sin que esto afecte a la sesión de autenticación.

Varios

Recarga activa

Realice actualizaciones rápidas de UI y código en aplicaciones en ejecución sin perder el estado de las aplicaciones a fin de lograr una experiencia más rápida y productiva para el desarrollador mediante Recarga activa. Para obtener más información, vea Compatibilidad de la Recarga activa de .NET con ASP.NET Core y Actualización del progreso de Recarga activa de .NET y aspectos destacados de Visual Studio 2022.

Plantillas de aplicación de página única (SPA) mejoradas

Se actualizaron las plantillas de proyecto de ASP.NET Core para Angular y React con el fin de usar un patrón mejorado para aplicaciones de página única que sea más flexible y que mejor se alinee con los patrones comunes para el desarrollo web de front-end moderno.

Anteriormente, la plantilla de ASP.NET Core para Angular y React usaba middleware especializado durante el desarrollo a fin de iniciar el servidor de desarrollo para el marco de front-end y, a continuación, redirigir mediante proxy las solicitudes desde ASP.NET Core hasta el servidor de desarrollo. La lógica para iniciar el servidor de desarrollo front-end era específica de la interfaz de línea de comandos para el marco de front-end correspondiente. Admitir marcos de front-end adicionales mediante este patrón significaba agregar una lógica adicional a ASP.NET Core.

Las plantillas actualizadas de ASP.NET Core para Angular y React en .NET 6 invierten esta disposición y aprovechan la compatibilidad integrada de proxy en los servidores de desarrollo de la mayoría de los marcos de front-end modernos. Cuando se inicia la aplicación de ASP.NET Core, el servidor de desarrollo front-end se inicia igual que antes, pero el servidor de desarrollo está configurado para redirigir mediante proxy las solicitudes al proceso de ASP.NET Core de back-end. Toda la configuración específica del front-end para configurar el proxy forma parte de la aplicación, no de ASP.NET Core. Ahora es sencillo configurar proyectos de ASP.NET Core para que funcionen con otros marcos de front-end: configure el servidor de desarrollo front-end para el marco elegido a fin de redirigir mediante proxy las solicitudes al back-end de ASP.NET Core mediante el patrón establecido en las plantillas de Angular y React.

El código de inicio de la aplicación ASP.NET Core ya no necesita ninguna lógica específica de aplicación de página única. La lógica para iniciar el servidor de desarrollo front-end durante el desarrollo se inserta en la aplicación en tiempo de ejecución mediante el paquete Microsoft.AspNetCore.SpaProxy nuevo. El enrutamiento de reserva se controla mediante el enrutamiento de puntos de conexión en lugar del middleware específico de SPA.

Las plantillas que siguen este patrón se pueden seguir ejecutando como un proyecto único en Visual Studio o mediante dotnet run en la línea de comandos. Cuando se publica la aplicación, el código de front-end de la carpeta ClientApp se compila y recopila tal como se hacía antes, en la raíz web de la aplicación host ASP.NET Core, y se implementa como archivos estáticos. Los archivos que están incluidos en la plantilla configuran el servidor de desarrollo front-end para usar HTTPS mediante el uso del certificador de desarrollo de ASP.NET Core.

Borrador de compatibilidad con HTTP/3 en .NET 6

Actualmente, HTTP/3 está en modo borrador y, por lo tanto, está sujeto a cambios. Todavía no se publica la compatibilidad con HTTP/3 en ASP.NET Core, sino que es una característica en vista previa (GB) incluida en .NET 6.

Consulte la entrada de blog sobre compatibilidad con HTTP/3 en .NET 6.

Anotaciones en los tipos de referencia nula

Se han aplicado anotaciones de nulabilidad a algunas partes del código fuente de ASP.NET Core 6.0.

Mediante el uso de la característica nueva de C# 8 que admite un valor NULL, ASP.NET Core puede proporcionar seguridad adicional en tiempo de compilación en el control de los tipos de referencia. Por ejemplo, protección frente a excepciones de referencia null. Los proyectos que han optado por utilizar anotaciones que admiten un valor NULL pueden ver advertencias en tiempo de compilación nuevas de las API de ASP.NET Core.

Si desea habilitar los tipos de referencia que admiten un valor NULL, agregue la propiedad siguiente a los archivos de proyecto:

<PropertyGroup>
    <Nullable>enable</Nullable>
</PropertyGroup>

Para más información, consulte Tipos de referencia que admiten un valor NULL.

Análisis de código fuente

Se agregaron varios analizadores de plataforma del compilador .NET que inspeccionan el código de la aplicación para buscar problemas como la configuración o el orden de middleware incorrecto, conflictos de enrutamiento, entre otros. Para más información, consulte Análisis de código en aplicaciones ASP.NET Core.

Mejoras en las plantillas de aplicación web

Las plantillas de aplicación web:

  • Usan el modelo de hospedaje mínimo nuevo.
  • Reducen considerablemente la cantidad de archivos y líneas de código que se necesitan para crear una aplicación. Por ejemplo, la aplicación web ASP.NET Core vacía crea un archivo de C# con cuatro líneas de código y es una aplicación completa.
  • Unifican Startup.cs y Program.cs en un archivo Program.cs único.
  • Utilizan instrucciones de nivel superior a fin de minimizar el código necesario para una aplicación.
  • Utilizan directivas using globales a fin de eliminar o minimizar la cantidad de líneas de instrucción using que se requieren.

Puertos generados por plantilla para Kestrel

Se asignan puertos aleatorios durante la creación del proyecto a fin de que los user el servidor web Kestrel. Los puertos aleatorios ayudan a minimizar la posibilidad de conflicto de puertos cuando se ejecutan varios proyectos en la misma máquina.

Cuando se crea un proyecto, se especifica un puerto HTTP aleatorio entre 5000 y 5300 y un puerto HTTPS aleatorio entre 7000 y 7300 en el archivo Properties/launchSettings.json generado. Puede cambiar los puertos en el archivo Properties/launchSettings.json. Si no se especifica ningún puerto, Kestrel utiliza de manera predeterminada los puertos HTTP 5000 y HTTPS 5001. Para más información, vea Configuración de puntos de conexión para el servidor web Kestrel de ASP.NET Core.

Valores predeterminados de registro nuevos

Se hicieron los cambios siguientes en appsettings.json y appsettings.Development.json:

- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Microsoft.AspNetCore": "Warning"

El cambio de "Microsoft": "Warning" a "Microsoft.AspNetCore": "Warning" genera el registro de todos los mensajes informativos del espacio de nombres MicrosoftexceptoMicrosoft.AspNetCore. Por ejemplo, Microsoft.EntityFrameworkCore ahora se registra en el nivel informativo.

Se agregó automáticamente el middleware de la página de excepciones para el desarrollador

En el entorno de desarrollo, DeveloperExceptionPageMiddleware se agrega de manera predeterminada. Ya no es necesario agregar el código siguiente a las aplicaciones de interfaz de usuario web:

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

Compatibilidad con encabezados de solicitud con codificación Latin1 en HttpSysServer

HttpSysServer ahora admite la descodificación de los encabezados de solicitud con codificación Latin1 mediante el establecimiento de la propiedad UseLatin1RequestHeaders de HttpSysOptions en true:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys(o => o.UseLatin1RequestHeaders = true);

var app = builder.Build();

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

app.Run();

Los registros del módulo ASP.NET Core incluyen marcas de tiempo y PID

Los registros de diagnóstico mejorados del módulo ASP.NET Core (ANCM) para IIS incluyen marcas de tiempo y PID del proceso que emite los registros. El registro de marcas de tiempo y PID facilita el diagnóstico de problemas con reinicios de procesos superpuestos en IIS cuando se ejecutan varios procesos de trabajo de IIS.

Los registros resultantes ahora son similares a la salida de ejemplo que se muestra a continuación:

[2021-07-28T19:23:44.076Z, PID: 11020] [aspnetcorev2.dll] Initializing logs for 'C:\<path>\aspnetcorev2.dll'. Process Id: 11020. File Version: 16.0.21209.0. Description: IIS ASP.NET Core Module V2. Commit: 96475a2acdf50d7599ba8e96583fa73efbe27912.
[2021-07-28T19:23:44.079Z, PID: 11020] [aspnetcorev2.dll] Resolving hostfxr parameters for application: '.\InProcessWebSite.exe' arguments: '' path: 'C:\Temp\e86ac4e9ced24bb6bacf1a9415e70753\'
[2021-07-28T19:23:44.080Z, PID: 11020] [aspnetcorev2.dll] Known dotnet.exe location: ''

Tamaño de búfer entrante no utilizado configurable para IIS

Anteriormente, el servidor IIS solo almacenaba en búfer 64 KiB de cuerpos de solicitud no utilizados. El almacenamiento en búfer de 64 KiB dio lugar a que las lecturas se limitaran a ese tamaño máximo, lo que afecta al rendimiento con cuerpos entrantes de gran tamaño, como las cargas. En .NET 6, el tamaño de búfer predeterminado cambia de 64 KiB a 1 MiB, lo que debería mejorar el rendimiento para cargas de gran tamaño. En las pruebas que hemos realizado, una carga de 700 MiB que solía tardar 9 s ahora solo tarda 2,5 s.

La desventaja de un tamaño de búfer mayor es que aumenta el consumo de memoria por solicitud cuando la aplicación no lee rápidamente desde el cuerpo de la solicitud. Por lo tanto, además de cambiar el tamaño de búfer predeterminado, el tamaño del búfer se puede configurar, lo que permite que las aplicaciones configuren el tamaño del búfer en función de la carga de trabajo.

Asistentes de etiquetas para componentes de vista

Imagine un componente de vista con un parámetro opcional, tal como se muestra en el código siguiente:

class MyViewComponent
{
    IViewComponentResult Invoke(bool showSomething = false) { ... }
}

Con ASP.NET Core 6, se puede invocar al asistente de etiquetas sin que sea necesario especificar un valor para el parámetro showSomething:

<vc:my />

La plantilla de Angular se actualizó a Angular 12

La plantilla de ASP.NET Core 6.0 para Angular ahora usa Angular 12.

La plantilla de React se actualizó a React 17.

Umbral de búfer configurable antes de escribir en el disco en el formateador de salida Json.NET

Nota: Se recomienda usar el formateador de salida System.Text.Json, excepto cuando se necesita el serializador Newtonsoft.Json por motivos de compatibilidad. El serializador System.Text.Json es completamente async y funciona de manera eficaz para cargas mayores.

De manera predeterminada, el formateador de salida Newtonsoft.Json almacena en búfer respuestas de hasta 32 KiB en memoria antes de realizar el almacenamiento en búfer en el disco. Esto es para evitar realizar E/S sincrónica, lo que puede generar otros efectos secundarios, como la falta de subprocesos e interbloqueos de aplicaciones. Sin embargo, si la respuesta tiene más de 32 KiB, se produce una E/S de disco considerable. Ahora, puede configurar el umbral de memoria con la propiedad MvcNewtonsoftJsonOptions.OutputFormatterMemoryBufferThreshold antes de realizar el almacenamiento en búfer en el disco:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages()
            .AddNewtonsoftJson(options =>
            { 
                options.OutputFormatterMemoryBufferThreshold = 48 * 1024;
            });

var app = builder.Build();

Para más información, consulte esta solicitud de incorporación de cambios de GitHub y el archivo NewtonsoftJsonOutputFormatterTest.cs.

Obtención y configuración más rápidas de encabezados HTTP

Se agregaron API nuevas para exponer todos los encabezados comunes disponibles en Microsoft.Net.Http.Headers.HeaderNames como propiedades IHeaderDictionary, lo que genera una API más fácil de usar. Por ejemplo, el middleware insertado en el código siguiente usa las API nuevas para obtener y configurar tanto los encabezados de solicitud como los de respuesta:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

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

app.Use(async (context, next) =>
{
    var hostHeader = context.Request.Headers.Host;
    app.Logger.LogInformation("Host header: {host}", hostHeader);
    context.Response.Headers.XPoweredBy = "ASP.NET Core 6.0";
    await next.Invoke(context);
    var dateHeader = context.Response.Headers.Date;
    app.Logger.LogInformation("Response date: {date}", dateHeader);
});

app.Run();

En el caso de los encabezados implementados, se implementan los descriptores de acceso get y set directamente en el campo, omitiendo la búsqueda. En el caso de los encabezados no implementados, los descriptores de acceso pueden omitir la búsqueda inicial respecto de los encabezados implementados y realizar directamente la búsqueda de Dictionary<string, StringValues>. Evitar la búsqueda genera un acceso más rápido en ambos escenarios.

Streaming asincrónico

ASP.NET Core ahora admite el streaming asincrónico de las acciones del controlador y las respuestas del formateador JSON. Devolver un objeto IAsyncEnumerable a partir de una acción ya no almacena en búfer el contenido de la respuesta en memoria antes de que se envíe. No almacenar en búfer ayuda a reducir el uso de memoria al devolver conjuntos de datos de gran tamaño que se pueden enumerar de manera asincrónica.

Tenga en cuenta que Entity Framework Core proporciona implementaciones de IAsyncEnumerable para consultar la base de datos. La compatibilidad mejorada con IAsyncEnumerable en ASP.NET Core en .NET 6 puede usar EF Core con ASP.NET Core con mayor eficacia. Por ejemplo, el código siguiente ya no almacena en búfer los datos del producto en memoria antes de enviar la respuesta:

public IActionResult GetMovies()
{
    return Ok(_context.Movie);
}

Sin embargo, al usar la carga diferida en EF Core, este comportamiento nuevo puede generar errores debido a la ejecución simultánea de consultas mientras se enumeran los datos. Las aplicaciones pueden almacenar en búfer los datos a fin de volver al comportamiento anterior:

public async Task<IActionResult> GetMovies2()
{
    return Ok(await _context.Movie.ToListAsync());
}

Consulte el anuncio relacionado para más detalles sobre este cambio en el comportamiento.

Middleware de registro HTTP

El registro HTTP es un middleware integrado nuevo que registra información sobre solicitudes HTTP y respuestas HTTP, incluidos los encabezados y todo el cuerpo:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();
app.UseHttpLogging();

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

app.Run();

Navegue a / con la información anterior sobre registros de código similar a esta salida:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/2
      Method: GET
      Scheme: https
      PathBase: 
      Path: /
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Cache-Control: max-age=0
      Connection: close
      Cookie: [Redacted]
      Host: localhost:44372
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30
      sec-ch-ua: [Redacted]
      sec-ch-ua-mobile: [Redacted]
      sec-ch-ua-platform: [Redacted]
      upgrade-insecure-requests: [Redacted]
      sec-fetch-site: [Redacted]
      sec-fetch-mode: [Redacted]
      sec-fetch-user: [Redacted]
      sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/plain; charset=utf-8

La salida anterior se habilitó con el archivo appsettings.Development.json siguiente:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
    }
  }
}

El registro HTTP proporciona registros de:

  • Información de solicitud HTTP
  • Propiedades comunes
  • Encabezados
  • Cuerpo
  • Información de respuesta HTTP

Si desea configurar el middleware de registro HTTP, especifique HttpLoggingOptions:

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(logging =>
{
    // Customize HTTP logging.
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("My-Request-Header");
    logging.ResponseHeaders.Add("My-Response-Header");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
});

var app = builder.Build();
app.UseHttpLogging();

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

app.Run();

IConnectionSocketFeature

La característica de solicitud IConnectionSocketFeature proporciona acceso al socket de aceptación subyacente asociado con la solicitud actual. Se puede obtener acceso a través de FeatureCollection en HttpContext.

Por ejemplo, la aplicación siguiente establece la propiedad LingerState en el socket aceptado:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
    {
        var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
        socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
        return next();
    }));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();

Restricciones de tipo genérico en Razor

Al definir parámetros de tipo genérico en Razor mediante la directiva @typeparam, ahora se pueden especificar las restricciones de tipo genérico mediante la sintaxis estándar de C#:

Scripts más pequeños de SignalR, Blazor Server y MessagePack

Los scripts de SignalR, MessagePack y Blazor Server ahora son considerablemente más pequeños, lo que permite realizar descargar más pequeñas, menos análisis y compilación de JavaScript por parte del explorador, además de un inicio más rápido. Reducciones de tamaño:

  • signalr.js: 70 %
  • blazor.server.js: 45 %

Los scripts más pequeños son el resultado de una contribución de la comunidad por parte de Ben Adams. Para más información sobre los detalles de la reducción de tamaño, consulte la solicitud de incorporación de cambios de GitHub de Ben.

Habilitación de sesiones de generación de perfiles en Redis

Una contribución de la comunidad de Gabriel Lucaci permite una sesión de generación de perfiles en Redis con Microsoft.Extensions.Caching.StackExchangeRedis:

using StackExchange.Redis.Profiling;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.ProfilingSession = () => new ProfilingSession();
});

Para más información, consulte el artículo sobre la generación de perfiles StackExchange.Redis.

Instantáneas en IIS

Se agregó una característica experimental al módulo ASP.NET Core (ANCM) para IIS a fin de agregar compatibilidad con los ensamblados de la aplicación de instantáneas. Actualmente, .NET bloquea los archivos binarios de la aplicación al ejecutarse en Windows, lo que hace imposible reemplazar los archivos binarios cuando se ejecuta la aplicación. Si bien nuestra recomendación sigue siendo usar un archivo de aplicación sin conexión, entendemos que hay escenarios en los que no es posible hacerlo; por ejemplo, en implementaciones de FTP.

En tales escenarios, habilite la instantánea mediante la personalización de la configuración del controlador del módulo ASP.NET Core. En la mayoría de los casos, las aplicaciones ASP.NET Core no tienen un elemento web.config incluido en el código fuente que pueda modificar. En ASP.NET Core, web.config lo suele generar el SDK. Para empezar, puede usar el web.config de ejemplo siguiente:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- To customize the asp.net core module uncomment and edit the following section. 
  For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->

  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
      <handlerSettings>
        <handlerSetting name="experimentalEnableShadowCopy" value="true" />
        <handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
        <!-- Only enable handler logging if you encounter issues-->
        <!--<handlerSetting name="debugFile" value=".\logs\aspnetcore-debug.log" />-->
        <!--<handlerSetting name="debugLevel" value="FILE,TRACE" />-->
      </handlerSettings>
    </aspNetCore>
  </system.webServer>
</configuration>

La instantánea en IIS es una característica experimental y no se garantiza que forme parte de ASP.NET Core. Deje comentarios sobre las instantáneas de IIS en este problema de GitHub.

Recursos adicionales