Новые возможности ASP.NET Core в .NET 6

В этой статье рассматриваются наиболее значительные изменения в ASP.NET Core в .NET 6 со ссылками на соответствующую документацию.

Улучшения ASP.NET Core MVC и Razor

Минимальные API

Архитектура минимальных API позволяет создавать API для HTTP с минимальным числом зависимостей. Они идеально подходят для микрослужб и приложений, которым нужен небольшой набор файлов, компонентов и зависимостей на платформе ASP.NET Core. Дополнительные сведения см. в разделе:

SignalR

Тег долгосрочной активности для подключений SignalR

SignalR использует новый объект Microsoft.AspNetCore.Http.Features.IHttpActivityFeature.Activity для добавления тега http.long_running к действию запроса. IHttpActivityFeature.Activity используется службами APM, такими как Azure Monitor Application Insights, чтобы фильтровать запросы SignalR и предотвращать создание предупреждений о длительных запросах.

Улучшения производительности SignalR

  • Распределить HubCallerClients один раз на соединение вместо каждого вызова метода концентратора.
  • Избегайте выделения закрытия в SignalRDefaultHubDispatcher.Invoke. Состояние передается в локальную статическую функцию через параметры, чтобы избежать выделения памяти для замыкания. Дополнительные сведения см. в этом пулл-реквесте на GitHub.
  • Выделение одного StreamItemMessage на поток, а не на каждый элемент потока в потоковой передаче с сервера клиенту. Дополнительные сведения см. в этом пулл-реквесте на GitHub.

Компилятор Razor

Компилятор Razor обновлен для использования генераторов исходного кода

Теперь компилятор Razor основан на генераторах исходного кода C#. Генераторы источников выполняются во время компиляции и проверяют, какие файлы компилируются для создания дополнительных файлов, скомпилированных вместе с остальной частью проекта. Использование генераторов исходного кода упрощает компилятор Razor и значительно сокращает время сборки.

Компилятор Razor больше не создает отдельную сборку Views

В компиляторе Razor ранее использовался двухэтапный процесс компиляции, при котором создавалась отдельная сборка Views, содержащая созданные представления и страницы (файлы .cshtml), определенные в приложении. Созданные типы были общедоступными и находились в пространстве имен AspNetCore.

Обновленный компилятор Razor создает представления и типы страниц в главной сборке проекта. Эти типы теперь по умолчанию создаются как внутренние запечатанные в пространстве имен AspNetCoreGeneratedDocument. Это изменение улучшает производительность сборки, позволяет развертывать один файл и позволяет этим типам участвовать в Горячей перезагрузке.

Дополнительные сведения об этом изменении см. в связанной теме объявления на GitHub.

улучшения производительности ASP.NET Core и API

Внесено множество изменений для уменьшения количества выделений и повышения производительности в стеке:

Уменьшение объема занимаемой памяти для бездействующих подключений TLS

Для длительных подключений TLS с редко передаваемыми данными мы значительно уменьшили объем памяти, занимаемый приложениями ASP.NET Core в .NET 6. Это поможет улучшить масштабируемость таких сценариев, как серверы WebSocket. Это стало возможно благодаря многочисленным улучшениям в System.IO.Pipelines, SslStream и Kestrel. В следующих разделах подробно описаны некоторые улучшения, которые привели к уменьшению объема занимаемой памяти.

Уменьшение размера System.IO.Pipelines.Pipe

Для каждого установленного соединения в Kestrel выделяются два канала:

  • транспортный уровень к приложению для обработки запроса.
  • уровень приложения к транспортировке для ответа.

За счет уменьшения размера System.IO.Pipelines.Pipe с 368 байт до 264 байт (примерно на 28,2%) обеспечивается экономия 208 байт на подключение (104 байт на канал).

Пул SocketSender

Объекты SocketSender (подкласс SocketAsyncEventArgs) имеют размер около 350 байт во время выполнения. Вместо выделения нового объекта SocketSender для каждого соединения их можно поместить в пул. Объекты SocketSender можно поместить в пул, так как отправка обычно выполняется очень быстро. Использование пулов сокращает затраты на подключение. Вместо выделения 350 байт на одно соединение, выделяется только 350 байт на IOQueue. Выделение выполняется по очереди во избежание конкуренции. На нашем сервере WebSocket с 5000 неактивными подключениями мы перешли от выделения ~1,75 МБ (350 байт * 5000) к выделению ~2,8 КБ (350 байт * 8) для объектов SocketSender.

Чтение нулевых байтов с SslStream

Операции чтения без буферизации — это методика, применяемая в ASP.NET Core, чтобы избежать аренды памяти из пула памяти, если на сокете нет доступных данных. До этого изменения наш сервер WebSocket с 5000 неактивными подключениями требовал 200 МБ без TLS по сравнению с 800 МБ с TLS. Некоторые из этих выделений (4 КБ на подключение) были вызваны необходимостью Kestrel удерживать ArrayPool<T> в буфере во время ожидания завершения операций чтения SslStream. Учитывая, что эти соединения были неактивными, ни одна из операций чтения не завершилась и не вернула буфер в ArrayPool, из-за чего ArrayPool нужно было выделять больше памяти. Оставшиеся выделения памяти были в самом SslStream: 4 КБ для подтверждений TLS и буфер 32 КБ для обычных операций чтения. В .NET 6, когда пользователь выполняет чтение нуля байтов на SslStream и не имеет доступных данных, SslStream внутренне выполняет чтение нуля байтов в обёрнутом базовом потоке. В лучшем случае (неактивное подключение) эти изменения приводят к экономии 40 КБ на подключение, одновременно позволяя объекту-получателю (Kestrel) получать уведомления о доступности данных без удержания неиспользуемых буферов.

Чтение нулевого байта с PipeReader

Поскольку для SslStream теперь поддерживаются операции чтения без буферизации, была добавлена возможность выполнять чтение нулевого количества байтов в StreamPipeReader, который является внутренним типом и преобразует Stream в PipeReader. В Kestrel элемент StreamPipeReader используется для адаптации базового SslStream в PipeReader. Поэтому было необходимо открыть семантику чтения нулевого байта на PipeReader.

Теперь можно создать объект PipeReader, который поддерживает чтение нулевого числа байт для любого используемого потока Stream, поддерживающего соответствующую семантику (например, SslStream, NetworkStream и др.) с помощью следующего API.

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

Удалите плиты из SlabMemoryPool

Чтобы уменьшить фрагментацию кучи, в Kestrel применялась методика выделения слэбов памяти размером 128 КБ в составе пула памяти. Затем эти слэбы делились на блоки размером 4 КБ, которые использовались внутри Kestrel. Размер этих slab должен был быть больше 85 КБ, чтобы принудительно выделить их в куче больших объектов и предотвратить перемещение этого массива в ходе ГЦ. Однако с появлением нового поколения метода сборки мусора, кучи закрепленных объектов (POH), больше не имеет смысла распределять блоки по slab (плита). Kestrel теперь непосредственно выделяет блоки на куче POH, уменьшая сложность, связанную с управлением пулом памяти. Это изменение должно упростить выполнение будущих улучшений, например упростить уменьшение пула памяти, используемого Kestrel.

Поддержка IAsyncDisposable

Теперь контроллеры, IAsyncDisposable Pages и компоненты представления могут использовать Razor. К соответствующим интерфейсам в фабриках и активаторах были добавлены асинхронные версии:

  • Новые методы предлагают реализацию интерфейса по умолчанию, которая передает выполнение синхронной версии и вызывает Dispose.
  • Реализации переопределяют реализацию по умолчанию и управляют процессом освобождения IAsyncDisposable.
  • Когда реализуются обе интерфейсы IAsyncDisposable и IDisposable, предпочтение отдается IAsyncDisposable.
  • Расширители должны переопределять новые методы, включаемые для поддержки экземпляров IAsyncDisposable.

IAsyncDisposable полезно использовать при работе с:

  • Асинхронные перечислители, например, в асинхронных потоках.
  • неуправляемыми ресурсами, имеющими ресурсоемкие операции ввода-вывода, требующие освобождения ресурсов.

При реализации этого интерфейса используйте метод DisposeAsync для освобождения ресурсов.

Рассмотрим контроллер, создающий и использующий Utf8JsonWriter. Utf8JsonWriter является ресурсом 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 должен реализовывать DisposeAsync:

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

    _jsonWriter = null;
}

Порт Vcpkg для клиента C++ SignalR

Vcpkg — это кроссплатформенный диспетчер пакетов командной строки для библиотек C и C++. Мы недавно добавили порт в vcpkg, чтобы обеспечить нативную поддержку CMake для клиента C++ SignalR. vcpkg также работает с MSBuild.

Клиент SignalR можно добавить в проект CMake с помощью следующего фрагмента кода, если vcpkg включен в файл цепочки инструментов:

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

С помощью предыдущего фрагмента SignalRклиент C++ готов к использованию #include и используется в проекте без дополнительной конфигурации. Полный пример приложения на C++, использующего SignalRклиент C++, можно найти в репозитории halter73/SignalR-Client-Cpp-Sample.

Blazor

Изменения шаблона проекта

Для приложений Blazor внесено несколько изменений в шаблон проекта, в частности, файл Pages/_Layout.cshtml использовался для компоновки содержимого, которое в более ранних приложениях _Host.cshtml отображалось в файле Blazor Server. Изучите изменения, создав приложение из шаблона проекта 6.0 или обратившись к справочному источнику ASP.NET Core по шаблонам проектов:

Blazor WebAssembly Поддержка встроенных зависимостей

Приложения Blazor WebAssembly могут использовать зависимости в машинном коде, созданные для выполнения в WebAssembly. Дополнительные сведения. см. в статье Собственные зависимости Blazor WebAssembly ASP.NET Core.

Компиляция Ahead-of-time (AOT) WebAssembly и повторная компоновка в среде выполнения

Blazor WebAssembly поддерживает упреждающую компиляцию (AOT), с помощью которой вы можете скомпилировать код .NET непосредственно в WebAssembly. Компиляция AOT позволяет повысить производительность среды выполнения за счет увеличения размера приложения. Перекомпоновка .NET WebAssembly исполняемой среды отсекает неиспользуемый код и улучшает скорость загрузки. Дополнительные сведения см. в разделах Компиляция Ahead-of-time (AOT) и Релинковка во время выполнения.

Продолжать сохранять предварительно визуализированное состояние

Blazor поддерживает сохранение состояния на предварительно отображенной странице, чтобы не нужно было повторно создавать состояние при полной загрузке приложения. Дополнительные сведения см. в разделе "Интеграция компонентов ASP.NET Core Razor с MVC или Razor Страницами".

Границы ошибок

Границы ошибок предоставляют удобный подход к обработке исключений на уровне пользовательского интерфейса. Дополнительные сведения см. в статье Обработка ошибок в приложениях Blazor ASP.NET Core.

Поддержка SVG

Элемент <foreignObject> поддерживается для отображения внутри SVG произвольного HTML-кода. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Поддержка Blazor Server передачи массива байтов в JS Interop

Blazor поддерживает оптимизированное взаимодействие с JS через массивы байтов, что позволяет избежать кодирования и декодирования массивов байтов в Base64. Дополнительные сведения см. на следующих ресурсах:

Усовершенствования строк запросов

Улучшена поддержка работы со строками запросов. Дополнительные сведения см. в разделе Навигация ASP.NET CoreBlazor.

Привязка для выбора нескольких элементов

Привязка поддерживает выбор нескольких вариантов с элементами <input>. Дополнительные сведения см. на следующих ресурсах:

Контроль содержимого <head> (head)

Компоненты Razor могут изменять содержимое элемента HTML <head> страницы, в том числе задавать заголовок страницы (элемент <title>) и изменять метаданные (элементы <meta>). Дополнительные сведения см. в разделе Управление содержимым <head> в приложениях Blazor ASP.NET Core.

Создание компонентов Angular и React

Создавать компоненты JavaScript, характерные для платформы, можно из компонентов Razor для веб-платформ, таких как Angular или React. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Отрисовка компонентов из JavaScript

Динамический рендеринг компонентов Razor из JavaScript для существующих приложений JavaScript. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Пользовательские элементы

Доступна экспериментальная поддержка для создания пользовательских элементов, использующих стандартные интерфейсы HTML. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Выведение универсальных типов компонентов из родительских компонентов

Компонент-предок может каскадировать параметр типа по имени к потомкам, используя новый атрибут [CascadingTypeParameter]. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Динамически отображаемые компоненты

Используйте новый встроенный компонент DynamicComponent для отображения компонентов по типу. Дополнительные сведения см. в разделе Динамически отображаемые компоненты Razor ASP.NET Core.

Улучшенная доступность Blazor

Используйте новый компонент FocusOnNavigate, чтобы установить фокус пользовательского интерфейса на элемент на основе селектора CSS после перехода с одной страницы на другую. Дополнительные сведения см. в разделе Маршрутизация ASP.NET CoreBlazor.

Поддержка аргументов пользовательских событий

Blazor поддерживает аргументы пользовательских событий, которые позволяют передавать произвольные данные в обработчики событий .NET с пользовательскими событиями. Дополнительные сведения см. в статье Обработка событий Blazor в ASP.NET Core.

Обязательные параметры

Примените новый атрибут [EditorRequired], чтобы указать обязательный параметр компонента. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Размещение файлов JavaScript рядом со страницами, представлениями и компонентами.

Размещайте совместно файлы JavaScript для страниц, представлений и компонентов Razor — это удобный способ организации скриптов в приложении. Дополнительные сведения см. в разделе Взаимодействие JavaScript Blazor ASP.NET Core (интероп JS).

Инициализаторы JavaScript

Инициализаторы JavaScript выполняют логику до и после загрузки приложения Blazor. Дополнительные сведения см. в разделе Взаимодействие JavaScript Blazor ASP.NET Core (интероп JS).

Потоковая передача при взаимодействии с JavaScript

Blazor теперь поддерживает потоковую передачу данных между .NET и JavaScript. Дополнительные сведения см. на следующих ресурсах:

Ограничения универсального типа

Теперь поддерживаются параметры универсального типа. Дополнительные сведения см. в статье Компоненты Razor ASP.NET Core.

Макет развертывания WebAssembly

Используйте макет развертывания, чтобы разрешить загрузку приложений Blazor WebAssembly в условиях ограниченной безопасности. Дополнительные сведения см. в разделе Макет развертывания для размещенных приложений ASP.NET CoreBlazor WebAssembly.

Новые Blazor статьи

В дополнение к функциям Blazor, описанным в предыдущих разделах, доступны новые статьи о Blazor по следующим темам:

Создание приложений Blazor Hybrid с помощью .NET MAUI, WPF и Windows Forms

Используйте Blazor Hybrid для объединения настольных и мобильных собственных клиентских платформ с .NET и Blazor:

  • .NET Multi-platform App UI (.NET MAUI) представляет собой кросс-платформенную платформу для создания собственных мобильных и настольных приложений с помощью C# и XAML.
  • Приложения Blazor Hybrid можно создавать с помощью платформ Windows Presentation Foundation (WPF) и Windows Forms.

Внимание

Blazor Hybrid находится на этапе предварительной версии и не следует использовать в рабочих приложениях до финального выпуска.

Дополнительные сведения см. на следующих ресурсах:

Kestrel

HTTP/3 в настоящее время находится в разработке, поэтому подлежит изменению. Поддержка HTTP/3 в ASP.NET Core не выпущена — это предварительная версия функции, включенная в .NET 6.

Kestrel теперь поддерживает HTTP/3. Дополнительные сведения см. в разделе Использование протокола HTTP/3 с веб-сервером Kestrel ASP.NET Core и запись в блоге Поддержка протокола HTTP/3 в .NET 6.

Новые категории логирования Kestrel для выбранного логирования

До этого изменения включение детализированного ведения журнала для Kestrel было чрезмерно дорогостоящим, так как всему Kestrel было присвоено общее имя категории журнала Microsoft.AspNetCore.Server.Kestrel. Microsoft.AspNetCore.Server.Kestrel по-прежнему можно использовать, но следующие новые подкатегории обеспечивают более полный контроль ведения журнала:

  • Microsoft.AspNetCore.Server.Kestrel (текущая категория): 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; Http2ConnectionClosedHttp2StreamErrorHttp2StreamResetAbortHPackDecodingErrorHPackEncodingErrorHttp2FrameReceivedHttp2FrameSendingHttp2MaxConcurrentStreamsReached
  • Microsoft.AspNetCore.Server.Kestrel.Http3: Http3ConnectionError, Http3ConnectionClosing, Http3ConnectionClosed, Http3StreamAbort, Http3FrameReceived, Http3FrameSending.

Существующие правила продолжают работать, но теперь можно более избирательно выбирать, какие правила включить. Например, можно значительно сократить издержки на наблюдения, если включить ведение журнала Debug только для недопустимых запросов. Для этого используется следующая конфигурация:

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

Фильтрация журналов применяет правила с наиболее длинным соответствующим префиксом категории. Дополнительные сведения см. в разделе Применение правил фильтрации.

Выдача KestrelServerOptions через событие EventSource

KestrelEventSource выдает новое событие, содержащее сериализованный в формате JSON KestrelServerOptions, при включении подробного режимаEventLevel.LogAlways. Это событие упрощает рассуждения о поведении сервера при анализе собранных трассировок. Ниже приведен пример полезной нагрузки события в формате JSON.

{
  "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"
    }
  ]
}

Новое событие DiagnosticSource для отклоненных HTTP-запросов

Kestrel теперь создает новое событие DiagnosticSource для HTTP-запросов, отклоненных на уровне сервера. До этого изменения возможности наблюдать за этими отклоненными запросами не было. Новое событие DiagnosticSourceMicrosoft.AspNetCore.Server.Kestrel.BadRequest содержит объект IBadRequestExceptionFeature, который можно использовать для анализа причины отклонения запроса.

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

Дополнительные сведения см. в разделе Ведение журнала и диагностика в Kestrel.

Создайте контекст соединения (ConnectionContext) из принятого сокета (Accept Socket)

Новый SocketConnectionContextFactory предоставляет возможность создать ConnectionContext на основе принятого сокета. Это предоставляет возможность создавать пользовательские решения, основанные на сокетах IConnectionListenerFactory, не теряя при этом все преимущества производительности и организации пулов в SocketConnection.

См. пример пользовательского IConnectionListenerFactory, в котором показано, как использовать SocketConnectionContextFactory.

Kestrel является профилем запуска по умолчанию для Visual Studio

Профиль запуска по умолчанию для всех новых веб-проектов dotnet — Kestrel. Запуск Kestrel значительно ускоряется, что повышает удобство при разработке приложений.

IIS Express по-прежнему можно использовать в качестве профиля запуска для таких сценариев, как проверка подлинности Windows или общий доступ к портам.

Порты localhost для Kestrel являются случайными

Дополнительные сведения см. в разделе Шаблон генерируемых портов для Kestrel в этом документе.

Проверка подлинности и авторизация

Серверы проверки подлинности

.NET 3 и .NET 5 использовали IdentityServer4 в рамках нашего шаблона для поддержки выдачи токенов JWT для SPA и Blazor приложений. Шаблоны теперь используют сервер Duende Identity.

Если вы расширяете модели идентификации и обновляете текущие проекты, измените пространства имен в своем коде с IdentityServer4.IdentityServer на Duende.IdentityServer и следуйте предоставленным инструкциям по миграции.

Модель лицензирования для Duende Identity Server была изменена на перекрестную лицензию, из-за чего может потребоваться оплатить лицензию, если она используется в рабочей среде в коммерческих целях. Дополнительные сведения см. на странице лицензии Duende.

Отложенное согласование сертификата клиента

Теперь разработчики могут согласиться на использование отложенного согласования сертификата клиента, указав ClientCertificateMode.DelayCertificate в HttpsConnectionAdapterOptions. Это работает только с подключениями HTTP/1.1, так как HTTP/2 запрещает отложенное повторное согласование сертификата. Вызывающий объект этого API должен поместить в буфер текст запроса, прежде чем запрашивать сертификат клиента:

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();

Скользящее истечение срока проверки подлинности Cookie теперь можно настроить или отключить, используя новый OnCheckSlidingExpiration. Например, это событие может использоваться одностраничным приложением, которое должно периодически выполнять проверку связи с сервером, не влияя на сеанс проверки подлинности.

Разное

Горячая перезагрузка

Горячая перезагрузка позволяет быстро вносить обновления в пользовательский интерфейс и код работающих приложений без потери состояния приложения, чтобы ускорить разработку и повысить ее продуктивность. Дополнительные сведения см. в статьях Поддержка Горячей перезагрузки .NET для ASP.NET Core и Обновление о ходе разработки Горячей перезагрузки .NET и основные моменты в Visual Studio 2022.

Улучшенные шаблоны одностраничных приложений (SPA)

Шаблоны проектов ASP.NET Core были обновлены для Angular и React. Теперь в них используется улучшенный шаблон для одностраничных приложений, которые обеспечивает большую гибкость и более точно соответствуют шаблонам для интерфейса современных веб-приложений.

Ранее в шаблоне ASP.NET Core для Angular и React во время разработки использовалось специализированное ПО промежуточного слоя для запуска сервера разработки для интерфейсной платформы, а затем запросы прокси от ASP.NET Core к серверу разработки. Логика запуска фронтенд-сервера разработки была предназначена для интерфейса командной строки соответствующего фронтенд-фреймворка. Для поддержки дополнительных интерфейсных платформ, использующих этот шаблон, нужно было добавлять дополнительную логику в ASP.NET Core.

Обновленные шаблоны ASP.NET Core для Angular и React в .NET 6 меняют используемую ранее схему и используют преимущества встроенной поддержки прокси-сервера на серверах разработки большинства современных интерфейсных платформ. При запуске приложения ASP.NET Core интерфейсный сервер разработки запускается так же, как и раньше, но сервер разработки настроен на передачу запросов через прокси-сервер к внутреннему процессу ASP.NET Core. Все настройки, связанные с настройкой прокси-сервера, являются частью приложения, а не ASP.NET Core. Настроить проекты ASP.NET Core для работы с другими фронтенд-фреймворками теперь просто: настройте сервер разработки для выбранного фреймворка так, чтобы он проксировал к ASP.NET Core бэкэнду по образцу, использованному в шаблонах Angular и React.

Для кода запуска приложения ASP.NET Core больше не нужна отдельная логика для одностраничного приложения. Логика для запуска интерфейсного сервера разработки во время разработки внедряется в приложение во время выполнения с помощью нового пакета Microsoft.AspNetCore.SpaProxy. Маршрутизация по умолчанию обрабатывается с помощью маршрутизации конечных точек вместо специализированного промежуточного ПО для одностраничных приложений.

Соответствующие шаблоны все еще можно запускать в виде отдельного проекта в Visual Studio или с помощью dotnet run из командной строки. При публикации приложения интерфейсный код в папке ClientApp строится и собирается, как и раньше, в корне узла приложения ASP.NET Core и предоставляется в виде статических файлов. Скрипты, содержащиеся в шаблоне, настраивают интерфейсный сервер разработки для использования протокола HTTPS с помощью сертификата разработки ASP.NET Core.

Предварительная поддержка HTTP/3 в .NET 6

HTTP/3 в настоящее время находится в разработке, поэтому подлежит изменению. Поддержка HTTP/3 в ASP.NET Core не выпущена — это предварительная версия функции, включенная в .NET 6.

См. запись блога Поддержка HTTP/3 в .NET 6.

Аннотации для ссылочных типов, допускающих значения NULL

Части ASP.NET Core в исходном коде .NET 6 имели применены заметки, допускавшие значение NULL .

Используя новую функцию допустимости значений NULL в C# 8, ASP.NET Core может обеспечить дополнительную безопасность во время компиляции при обработке ссылочных типов. Например, защита от исключений ссылок на null. В проектах, которые выбрали использование заметок о допустимости значений NULL, могут появиться новые предупреждения времени сборки от API ASP.NET Core.

Чтобы включить ссылочные типы, допускающие значения NULL, добавьте в файлы проекта следующее свойство:

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

Дополнительные сведения см. в статье Ссылочные типы, допускающие значение NULL.

Анализ исходного кода

Были добавлены несколько анализаторов платформы компилятора .NET, которые проверяют код приложения на наличие проблем, таких как неправильная конфигурация промежуточного программного обеспечения или его порядок, конфликты маршрутизации и т. д. Дополнительные сведения см. в разделе "Анализ диагностического кода" в приложениях ASP.NET Core.

Улучшения шаблонов веб-приложений

Шаблоны веб-приложений:

  • Используют новую минимальную модель размещения.
  • Значительно сокращают количество файлов и строк кода, необходимых для создания приложения. Например, пустое веб-приложение ASP.NET Core создает один файл C# с четырьмя строками кода и является полноценным приложением.
  • Объединяют Startup.cs и Program.cs в один файл Program.cs.
  • Используют инструкции верхнего уровня для минимизации кода, необходимого для приложения.
  • Используют глобальные директивы using, чтобы исключить или минимизировать числу требуемых строк инструкций using.

Порты, созданные на основе шаблона для Kestrel

Во время создания проекта назначаются случайные порты для использования веб-сервером Kestrel. Случайные порты помогают уменьшить конфликт портов, если на одном компьютере выполняется несколько проектов.

При создании проекта случайный HTTP-порт от 5000 до 5300 и случайный порт HTTPS от 7000 до 7300 указывается в созданном Properties/launchSettings.json файле. Порты можно изменить в Properties/launchSettings.json файле. Если порт не указан, Kestrel по умолчанию использует порт HTTP 5000 и HTTPS 5001. Дополнительные сведения см. в разделе "Настройка конечных точек для Kestrel веб-сервера".

Новые параметры ведения журнала по умолчанию

В appsettings.json и appsettings.Development.json были внесены следующие изменения:

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

Изменение от "Microsoft": "Warning" приводит к ведению журнала всех информационных сообщений из пространства имен "Microsoft.AspNetCore": "Warning", кроме Microsoft. Например, Microsoft.EntityFrameworkCore теперь заносится в журнал на уровне информации.

Автоматическое добавление промежуточного ПО для страницы обработки исключений разработчика

В среде разработки добавляется по умолчанию. Больше не нужно добавлять следующий код в приложения пользовательского веб-интерфейса:

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

Поддержка заголовков запросов в кодировке Latin1 в HttpSysServer

HttpSysServer теперь поддерживает декодирование заголовков запросов, Latin1 закодированных путем присвоения свойству UseLatin1RequestHeaders в HttpSysOptions значения true:

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

var app = builder.Build();

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

app.Run();

Журналы модуля ASP.NET Core включают метки времени и идентификатор процесса

Расширенные журналы диагностики модуля ASP.NET Core (ANCM) для IIS (ANCM) содержат метки времени и идентификатор процесса, создающего журналы. Запись меток времени и идентификатора процесса в журналы упрощает диагностику проблем с перекрывающимися перезапусками процессов в IIS при выполнении нескольких рабочих процессов IIS.

Полученные журналы теперь выглядят как пример, показанный ниже:

[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: ''

Настраиваемый размер буфера для неиспользованных входящих запросов для IIS

Сервер IIS ранее помещал в буфер только 64 КБ текстов неиспользованных запросов. Размер буфера 64 КБ привел к ограничению операций чтения этим максимальным размером, что влияло на производительность при больших входящих объектах, таких как отправка данных. В .NET 6 размер буфера по умолчанию измене с 64 КиБ на 1 МиБ, что повышает пропускную способность для отправок больших объемов данных. В наших тестах отправка 700 МиБ, которая занимала 9 секунд, теперь занимает всего 2,5 секунды.

Недостатком большего размера буфера является увеличение потребления памяти для каждого запроса, когда приложение не считывает текст запроса быстро. Поэтому, помимо изменения размера буфера по умолчанию, теперь появилась возможность настроить размер буфера для приложений в зависимости от рабочей нагрузки.

Вспомогательные теги для компонентов представлений

Рассмотрим компонент представления с необязательным параметром, как показано в следующем коде:

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

При использовании ASP.NET Core в .NET 6 вспомогательный компонент тега можно вызвать без необходимости указывать значение для showSomething параметра:

<vc:my />

Шаблон Angular обновлен до версии Angular 12

Шаблон ASP.NET Core в .NET 6 для Angular теперь использует Angular 12.

Шаблон React обновлен до React  17.

Настраиваемое пороговое значение буфера перед записью на диск в форматировщике выходных данных Json.NET

Примечание. Рекомендуется использовать форматировщик выходных данных System.Text.Json, за исключением случаев, когда сериализатор Newtonsoft.Json требуется для обеспечения совместимости. Сериализатор System.Text.Json является полностью async и эффективно работает с большими объемами данных.

Форматировщик выходных данных Newtonsoft.Json по умолчанию помещает в буфер в памяти ответы размером до 32 КБ перед буферизацией на диске. Это позволяет избежать выполнения синхронных операций ввода-вывода, которые могут привести к другим побочным эффектам, таким как нехватка потоков и взаимоблокировка приложений. Однако если размер ответа превышает 32 КБ, для операций дискового ввода-вывода требуется значительное время. Теперь пороговое использование памяти, прежде чем данные помещаются в буфер на диске, можно настроить с помощью свойства MvcNewtonsoftJsonOptions.OutputFormatterMemoryBufferThreshold:

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

Дополнительные сведения см. в этом pull-запросе на GitHub и в файле NewtonsoftJsonOutputFormatterTest.cs.

Ускорение получения и установки заголовков HTTP

Добавлены новые API для предоставления всех общих заголовков, доступных на Microsoft.Net.Http.Headers.HeaderNames, в виде свойств в IHeaderDictionary, что упрощает использование API. Например, встроенное ПО промежуточного слоя в следующем коде получает и устанавливает заголовки запросов и ответов с помощью новых API:

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();

Для заголовков, доступ к которым реализован, методы get и set осуществляются путем обращения непосредственно к полю, обходя механизм поиска. Для нереализованных заголовков методы доступа могут пропускать первоначальный поиск по реализованным заголовкам и напрямую выполнять поиск Dictionary<string, StringValues>. Пропуск поиска ускоряет доступ в обоих сценариях.

Асинхронная потоковая передача

ASP.NET Core теперь поддерживает асинхронную потоковую передачу от действий и ответов контроллера из форматировщика JSON. Возврат IAsyncEnumerable из действия больше не приводит к буферизации содержимого ответа в оперативной памяти перед его отправкой. Отсутствие буферизации помогает сократить использование памяти при возврате больших наборов данных, которые можно перечислить асинхронно.

Обратите внимание, что Entity Framework Core предоставляет реализации IAsyncEnumerable для выполнения запросов к базе данных. Улучшенная поддержка IAsyncEnumerable в ASP.NET Core в .NET 6 может сделать использование EF Core с ASP.NET Core более эффективным. Например, следующий код больше не помещает данные о продуктах в память перед отправкой ответа:

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

Однако при использовании отложенной загрузки EF Coreэто новое поведение может привести к ошибкам из-за параллельного выполнения запроса во время перечисления данных. Приложения могут вернуться к предыдущему поведению путем буферизации данных:

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

Дополнительные сведения об этом изменении в поведении см. в соответствующем объявлении.

Среда ведения журнала HTTP

Ведение журнала HTTP — это новое встроенное ПО промежуточного слоя, в котором регистрируются сведения об HTTP-запросах и HTTP-ответах, включая заголовки и весь текст:

var builder = WebApplication.CreateBuilder(args);

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

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

app.Run();

При переходе к / с помощью кода выше данные записываются в журнал примерно так, как показано ниже:

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

Предыдущие выходные данные активированы с помощью следующего файла appsettings.Development.json.

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

Ведение журнала HTTP предоставляет журналы со следующими сведениями:

  • информация HTTP-запроса;
  • Общие свойства
  • Заголовки
  • Основной текст
  • информация HTTP-ответа.

Чтобы настроить ПО промежуточного слоя для ведения журнала HTTP, укажите 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

Функция запроса IConnectionSocketFeature предоставляет доступ к базовому принимающему сокету, связанному с текущим запросом. Использовать ее можно с помощью FeatureCollection в HttpContext.

Например, следующее приложение задает свойство LingerState для принимающего сокета:

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();

Ограничения универсального типа в Razor

При определении параметров универсального типа в Razor с помощью директивы @typeparam ограничения универсального типа теперь можно указать с помощью стандартного синтаксиса C#:

Уменьшение размера скриптов SignalR, Blazor Server и MessagePack

Скрипты SignalR, MessagePack и Blazor Server теперь значительно меньше, что обеспечивает меньший объем загрузки, меньшее время синтаксического анализа и компиляции кода JavaScript в браузере и ускоряет запуск. Уменьшение размера:

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

Уменьшение размера скриптов стало возможным благодаря Бену Адамсу (Ben Adams). Дополнительные сведения об уменьшении размера см. в запросе на вытягивание Бена на GitHub.

Включение сеансов профилирования Redis

Вклад пользователя Габриэля Лукачи (Gabriel Lucaci) позволяет проводить сессии профилирования Redis с помощью Microsoft.Extensions.Caching.StackExchangeRedis:

using StackExchange.Redis.Profiling;

var builder = WebApplication.CreateBuilder(args);

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

Дополнительные сведения см. в статье Профилирование StackExchange.Redis.

Теневое копирование в IIS

В модуль ASP.NET Core (ANCM) для IIS добавлена экспериментальная функция, позволяющая обеспечить поддержку теневого копирования сборок приложений. В настоящее время .NET блокирует двоичные файлы приложения при запуске на Windows, что делает невозможным замену двоичных файлов во время работы приложения. Хотя мы по-прежнему рекомендуем использовать автономный файл приложения, мы понимаем, что существуют определенные сценарии (например, FTP-развертывания), где это невозможно.

В таких случаях включите теневое копирование, настроив параметры обработчика модуля ASP.NET Core. В большинстве случаев у приложений ASP.NET Core нет файла web.config в системе управления версиями, который можно изменить. В ASP.NET Core web.config обычно создается пакетом SDK. Чтобы приступить к работе, можно использовать следующий пример web.config:

<?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>

Теневое копирование в IIS — это экспериментальная функция. Мы не гарантируем, что она станет частью ASP.NET Core. Оставьте отзыв о теневом копировании IIS в описании этой проблемы на GitHub.

Кардинальные изменения

Используйте статьи в статье Критические изменения в .NET, чтобы найти критические изменения , которые могут применяться при обновлении приложения до более новой версии .NET.

Дополнительные ресурсы