Новые возможности в ASP.NET Core 6.0
В этой статье описываются наиболее важные изменения в ASP.NET Core 6.0 со ссылками на соответствующую документацию.
Улучшения ASP.NET Core MVC и Razor
Минимальные API
Архитектура минимальных API позволяет создавать API для HTTP с минимальным числом зависимостей. Они идеально подходят для микрослужб и приложений, которым нужен небольшой набор файлов, компонентов и зависимостей на платформе ASP.NET Core. Дополнительные сведения см. в разделе:
- Руководство. Создание минимального API с помощью ASP.NET Core
- Различия между минимальными API и API с контроллерами
- Краткий справочник по минимальным API
- Примеры кода перенесены на новую минимальную модель размещения в 6.0
SignalR
Тег длительно выполняемого действия для подключений SignalR
SignalR использует новый объект Microsoft.AspNetCore.Http.Features.IHttpActivityFeature.Activity для добавления тега http.long_running
к действию запроса. IHttpActivityFeature.Activity
используется службами APM, например Azure Monitor Application Insights для фильтрации запросов SignalR на создание предупреждений о длительных запросах.
Улучшения производительности SignalR
- Распределить HubCallerClients один раз на соединение вместо каждого вызова метода концентратора.
- Устранено выделение закрытия в SignalR
DefaultHubDispatcher.Invoke
. Состояние передается в локальную статическую функцию через параметры, чтобы избежать выделения закрытия. Дополнительные сведения см. в этом запросе на вытягивание на GitHub. - Выделение одного StreamItemMessage на поток, а не на каждый элемент потока в потоковой передаче с сервера клиенту. Дополнительные сведения см. в этом запросе на вытягивание на GitHub.
Компилятор Razor
Компилятор Razor обновлен для использования генераторов исходного кода
Теперь компилятор Razor основан на генераторах исходного кода C#. Генераторы источников выполняются во время компиляции и проверяют, какие данные компилируются для создания дополнительных файлов, скомпилированных вместе с проектом rest . Использование генераторов исходного кода упрощает компилятор Razor и значительно сокращает время сборки.
Компилятор Razor больше не создает отдельную сборку Views
В компиляторе Razor ранее использовался двухэтапный процесс компиляции, при котором создавалась отдельная сборка Views, содержащая созданные представления и страницы (файлы .cshtml
), определенные в приложении. Созданные типы были общедоступными и находились в пространстве имен AspNetCore
.
Обновленный компилятор Razor создает представления и типы страниц в главной сборке проекта. Эти типы теперь по умолчанию создаются как внутренние запечатанные в пространстве имен AspNetCoreGeneratedDocument
. Это изменение улучшает производительность сборки, позволяет развертывать один файл и позволяет этим типам участвовать в Горячей перезагрузке.
Дополнительные сведения об этом изменении см. в этой статье об ошибке на GitHub.
улучшения производительности ASP.NET Core и API
Внесено множество изменений для уменьшения количества выделений и повышения производительности в стеке:
- Метод расширения app.Use без выделения. Новая перегрузка метода
app.Use
требует передачи контекста вnext
, который сохраняет два внутренних выделения памяти для каждого запроса, которые трубеются при другой перегрузке. - Уменьшение количества распределений памяти при получении доступа к HttpRequest.Cookies. Дополнительные сведения см. здесь на GitHub.
- Используйте LoggerMessage.Define только для веб-сервера HTTP.sys Windows. Вызовы методов расширения ILogger были заменены вызовами
LoggerMessage.Define
. - Снизить накладные расходы на каждое соединение в SocketConnection на ~30 %. Дополнительные сведения см. в этом запросе на вытягивание на GitHub.
- Сокращено количество выделений за счет удаления делегатов ведения журналов в универсальных типах. Дополнительные сведения см. в этом запросе на вытягивание на GitHub.
- Более быстрый доступ GET (примерно на 50% быстрее) к часто используемым функциям, включая IHttpRequestFeature, IHttpResponseFeature, IHttpResponseBodyFeature, IRouteValuesFeature и IEndpointFeature. Дополнительные сведения см. в этом запросе на вытягивание на GitHub.
- Использование строк одного экземпляра для известных имен заголовков, даже если они не находятся в сохраненном блоке заголовка. Использование строки с одним экземпляром помогает предотвратить несколько дубликатов одной строки в долгосрочных соединениях, например в Microsoft.AspNetCore.WebSockets. Дополнительные сведения см. здесь на GitHub.
- Повторно используйте HttpProtocol CancellationTokenSource в Kestrel. Используйте новый метод CancellationTokenSource.TryReset для
CancellationTokenSource
, чтобы повторно использовать токены, если они не были отменены. Дополнительные сведения см. в описании этой ошибки на GitHub и в этом видео. - Реализуйте и используйте AdaptiveCapacityDictionary в Microsoft.AspNetCore.Http RequestCookieCollectionion для более эффективного доступа к словарям. Дополнительные сведения см. в этом запросе на вытягивание на GitHub.
Уменьшение объема занимаемой памяти для бездействующих подключений 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));
Удаление 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. Дополнительные сведения см. на следующих ресурсах:
- Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor
- Вызов методов .NET из функций JavaScript в Blazor ASP.NET Core
Усовершенствования строк запросов
Улучшена поддержка работы со строками запросов. Дополнительные сведения см. в статье Маршрутизация 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[]
для потоковой передачи, чтобы обеспечить эффективную передачу данных клиенту. - Отображение изображений и документов в ASP.NET Core 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 находится на этапе предварительной версии и не должна использоваться в рабочих приложениях до финального выпуска.
Дополнительные сведения см. на следующих ресурсах:
- Ознакомьтесь с документацией по ASP.NET Core Blazor Hybrid
- Что такое .NET MAUI?
- Блог Microsoft .NET (категория: ".NET MAUI")
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
NotAllConnectionsAborted
ConnectionPause
ConnectionDisconnect
ConnectionResume
ConnectionKeepAlive
ConnectionRejected
NotAllConnectionsClosedGracefully
.ApplicationAbortedConnection
Microsoft.AspNetCore.Server.Kestrel.Http2
:Http2ConnectionError
,Http2ConnectionClosing
;Http2ConnectionClosed
Http2MaxConcurrentStreamsReached
Http2StreamError
Http2StreamResetAbort
HPackDecodingError
HPackEncodingError
Http2FrameReceived
Http2FrameSending
Microsoft.AspNetCore.Server.Kestrel.Http3
:Http3ConnectionError
, ,Http3ConnectionClosed
Http3FrameReceived
Http3ConnectionClosing
Http3StreamAbort
, , .Http3FrameSending
Существующие правила продолжают работать, но теперь можно более избирательно включать правила. Например, можно значительно сократить издержки на наблюдения, если включить ведение журнала Debug
только для недопустимых запросов. Для этого используется следующая конфигурация:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Kestrel.BadRequests": "Debug"
}
}
Фильтрация журналов применяет правила с наиболее длинным соответствующим префиксом категории. Дополнительные сведения см. в разделе Применение правил фильтрации.
Выдача KestrelServerOptions через событие EventSource
KestrelEventSource выдает новое событие, содержащее сериализованный KestrelServerOptions JSON при включении детализации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-запросов, отклоненных на уровне сервера. До этого изменения возможности наблюдать за этими отклоненными запросами не было. Новое событие DiagnosticSource
Microsoft.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 в .NET 5 использовал IdentityServer4 в рамках нашего шаблона для поддержки выдачи токенов JWT для SPA и Blazor приложений. Шаблоны теперь используют сервер Duende Identity.
Если вы расширяете 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();
OnCheckSlidingExpiration
событие для контроля обновления cookie
Скользящий срок действия проверки подлинности 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
пространства имен, кроме Microsoft.AspNetCore
."Microsoft.AspNetCore": "Warning"
Например, 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.
Дополнительные ресурсы
ASP.NET Core