Поделиться через


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

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

Улучшения 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. В KestrelStreamPipeReader используется для адаптации базового SslStream в PipeReader. Поэтому было необходимо предоставить эту семантику чтения нуля байтов в PipeReader.

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

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

Удаление slab из SlabMemoryPool

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

Поддержка IAsyncDisposable

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

  • Новые методы предлагают реализацию интерфейса по умолчанию, которая делегирует синхронную версию и вызывает Dispose.
  • Реализации переопределяют реализацию по умолчанию и обрабатывают реализации уничтожения IAsyncDisposable.
  • Если реализован IAsyncDisposable и IDisposable, реализации предпочитают первый интерфейс.
  • Расширители должны переопределять новые методы, включаемые для поддержки экземпляров 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 использовался для компоновки содержимого, которое в более ранних приложениях Blazor Server отображалось в файле _Host.cshtml. Изучите изменения, создав приложение из шаблона проекта 6.0 или обратившись к справочному источнику ASP.NET Core по шаблонам проектов:

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

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

Компиляция AOT и повторная компоновка во время выполнения WebAssembly

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

Сохранение предварительно отрисованного состояния

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

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

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

Поддержка SVG

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

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

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

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

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

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

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

Управление содержимым элемента <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 Core Blazor и навигация.

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

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 размещенных Blazor WebAssembly приложений Core.

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

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

  • Загрузки файлов Blazor ASP.NET Core: узнайте, как скачать файл, используя собственное взаимодействие byte[] для потоковой передачи, чтобы обеспечить эффективную передачу данных клиенту.
  • Работа с изображениями в Blazor ASP.NET Core: узнайте, как работать с изображениями в приложениях 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, ConnectionStartConnectionStopNotAllConnectionsAbortedConnectionPauseConnectionDisconnectConnectionResumeConnectionKeepAliveConnectionRejectedNotAllConnectionsClosedGracefully. ApplicationAbortedConnection
  • Microsoft.AspNetCore.Server.Kestrel.Http2: Http2ConnectionError, Http2ConnectionClosing; Http2ConnectionClosedHttp2MaxConcurrentStreamsReachedHttp2StreamErrorHttp2StreamResetAbortHPackDecodingErrorHPackEncodingErrorHttp2FrameReceivedHttp2FrameSending
  • Microsoft.AspNetCore.Server.Kestrel.Http3: Http3ConnectionError, , Http3ConnectionClosedHttp3FrameReceivedHttp3ConnectionClosingHttp3StreamAbort, , . 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 на основе принятого приема

SocketConnectionContextFactory позволяет создать ConnectionContext на основе принятого сокета. Это позволяет создавать пользовательские IConnectionListenerFactory на основе сокетов, не теряя при этом производительность и организацию пулов в SocketConnection.

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

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

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

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

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

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

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

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

В версиях .NET от 3 до 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 6,0 были применены заметки о допустимости значений 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Настройка конечных точек для веб-сервера для ASP.NET Core.

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

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

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

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

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

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

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 6 вспомогательную функцию тегов можно вызвать без указания значения для параметра showSomething:

<vc:my />

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

Шаблон ASP.NET Core 6.0 для 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();

Дополнительные сведения см. в этом запросе на вытягивание на 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();

Для реализованных заголовков методы доступа получения и установки реализуются путем перехода непосредственно к полю без поиска. Для нереализованных заголовков методы доступа могут пропускать первоначальный поиск по реализованным заголовкам и напрямую выполнять поиск 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.

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