Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье рассматриваются самые значительные изменения в ASP.NET Core в .NET 10 со ссылками на соответствующую документацию.
Blazor
В этом разделе описываются новые функции для Blazor.
Новые и обновленные Blazor Web App примеры безопасности
Мы добавили и обновили Blazor Web App образцы безопасности, упомянутые в следующих статьях:
- Обеспечить безопасную работу ASP.NET Core Blazor Web App с OpenID Connect (OIDC)
- Защитите ASP.NET Core Blazor Web App с помощью Microsoft Entra ID
- Защитите ASP.NET Core Blazor Web App с помощью проверки подлинности Windows
Все наши примеры решений OIDC и Entra теперь включают отдельный проект веб-API (MinimalApiJwt), чтобы продемонстрировать, как настроить и вызвать внешний веб-API безопасно. Вызов веб-API демонстрируется с помощью обработчика маркеров и именованного HTTP-клиента для поставщика удостоверений OIDC или Microsoft Identity веб-пакетов или API для Microsoft Entra ID.
Примеры решений настраиваются в файлах Program с помощью кода на C#. Сведения о настройке решений из файлов параметров приложения (например, appsettings.json) см. в новом разделе "Конфигурация поставки" с помощью поставщика конфигурации JSON (параметров приложения) статьи OIDC или Entra.
В нашей статье о Entra и примерах приложений также содержатся новые рекомендации по следующим методам:
- Как использовать зашифрованный распределенный кэш токенов в сценариях размещения серверных ферм.
- Как использовать Azure Key Vault с управляемыми удостоверениями Azure Active Directory для защиты данных.
параметр QuickGridRowClass
Примените класс стилей оформления к строке сетки, основанной на элементе строки, с помощью нового параметра RowClass. В следующем примере метод GetRowCssClass вызывается для каждой строки для условного применения класса таблицы стилей на основе элемента строки:
<QuickGrid ... RowClass="GetRowCssClass">
...
</QuickGrid>
@code {
private string GetRowCssClass(MyGridItem item) =>
item.IsArchived ? "row-archived" : null;
}
Дополнительные сведения см. в разделе ASP.NET Core Blazor компонент QuickGrid.
скрипт Blazor как статический веб-ресурс
В предыдущих выпусках .NET скрипт Blazor обслуживается из внедренного ресурса в ASP.NET Core общей платформы. Начиная с .NET 10, скрипт Blazor представлен как статический веб-ресурс с автоматическим сжатием и фингерпринтингом.
Скрипт Blazor (blazor.web.js или blazor.server.js) включается платформой, если проект содержит по крайней мере один Razor файл компонента (.razor). Если вашему приложению требуется скрипт Blazor, но оно не содержит хотя бы одного компонента, добавьте следующее свойство MSBuild в файл проекта приложения, чтобы принудительно включить безусловный скрипт:
<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>
Дополнительные сведения см. в следующих ресурсах:
Основные сведения о шаблоне маршрута
Теперь [Route] атрибут поддерживает выделение синтаксиса маршрута для визуализации структуры шаблона маршрута:
NavigateTo больше не прокручивается наверх при навигации по той же странице
Ранее NavigationManager.NavigateTo прокручивался в начало страницы при навигации на той же странице. Это поведение было изменено в .NET 10, чтобы браузер больше не прокручивался наверх страницы, когда осуществляется навигация на ту же страницу. Это означает, что окно просмотра больше не сбрасывается при изменении адреса текущей страницы, например, при изменении строки запроса или фрагмента.
Компонент пользовательского интерфейса повторного подключения, добавленный в шаблон проекта Blazor Web App
Теперь шаблон проекта Blazor Web App включает компонент ReconnectModal, включая составную таблицу стилей и файлы JavaScript, для улучшения контроля разработчика над пользовательским интерфейсом повторного подключения, когда клиент теряет подключение WebSocket к серверу. Компонент не вставляет стили программным образом, обеспечивая соответствие более строгим параметрам политики безопасности содержимого (CSP) для политики style-src. В предыдущих выпусках пользовательский интерфейс повторного подключения по умолчанию был создан платформой таким образом, что может привести к нарушениям CSP. Обратите внимание, что пользовательский интерфейс повторного подключения по умолчанию по-прежнему используется в качестве резервного элемента, если приложение не определяет пользовательский интерфейс повторного подключения, например с помощью компонента ReconnectModal шаблона проекта или аналогичного пользовательского компонента.
Новые возможности пользовательского интерфейса повторного подключения:
- Помимо указания состояния повторного подключения, задав определенный класс CSS в элементе пользовательского интерфейса повторного подключения, новое событие
components-reconnect-state-changedотправляется для изменения состояния повторного подключения. - Код может лучше различать этапы процесса повторного подключения с новым состоянием повторного подключения "
retrying", указанным как классом CSS, так и новым событием.
Дополнительные сведения см. в разделе ASP.NET Core BlazorSignalR руководство.
Игнорировать строку запроса и фрагмент при использовании NavLinkMatch.All
Компонент NavLink теперь игнорирует строку запроса и фрагмент при использовании значения NavLinkMatch.All для параметра Match. Это означает, что ссылка сохраняет класс active, если путь URL совпадает, но строка запроса или фрагмент изменяется. Чтобы вернуться к исходному поведению, используйте переключатель Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragmentAppContext, установленный на true.
Можно также переопределить метод ShouldMatch на NavLink, чтобы настроить поведение сопоставления:
public class CustomNavLink : NavLink
{
protected override bool ShouldMatch(string currentUriAbsolute)
{
// Custom matching logic
}
}
Дополнительные сведения см. в разделе ASP.NET Core Blazor навигации.
Закрыть параметры столбца QuickGrid
Теперь можно закрыть пользовательский интерфейс параметров столбца QuickGrid с помощью нового метода HideColumnOptionsAsync.
В следующем примере метод HideColumnOptionsAsync используется для закрытия пользовательского интерфейса параметров столбца сразу после применения фильтра заголовков:
<QuickGrid @ref="movieGrid" Items="movies">
<PropertyColumn Property="@(m => m.Title)" Title="Title">
<ColumnOptions>
<input type="search" @bind="titleFilter" placeholder="Filter by title"
@bind:after="@(() => movieGrid.HideColumnOptionsAsync())" />
</ColumnOptions>
</PropertyColumn>
<PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
<PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
</QuickGrid>
@code {
private QuickGrid<Movie>? movieGrid;
private string titleFilter = string.Empty;
private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
private IQueryable<Movie> filteredMovies =>
movies.Where(m => m.Title!.Contains(titleFilter));
}
Включение потоковой передачи ответов в HttpClient по умолчанию
В предыдущих Blazor выпусках потоковая передача ответов для HttpClient запросов была опциональной. Теперь потоковая передача ответов включена по умолчанию.
Это является серьезным изменением, так как вызов HttpContent.ReadAsStreamAsync для HttpResponseMessage.Content (response.Content.ReadAsStreamAsync()) возвращает BrowserHttpReadStream и больше не возвращает MemoryStream.
BrowserHttpReadStream не поддерживает синхронные операции, например Stream.Read(Span<Byte>). Если код использует синхронные операции, вы можете отказаться от потоковой передачи ответов или скопировать Stream в MemoryStream самостоятельно.
Чтобы отказаться от потоковой передачи ответов глобально, используйте любой из следующих подходов:
Добавьте свойство
<WasmEnableStreamingResponse>в файл проекта со значениемfalse:<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>Установите для переменной среды
DOTNET_WASM_ENABLE_STREAMING_RESPONSEзначениеfalseили0.
Чтобы отказаться от потоковой передачи ответов для отдельного запроса, задайте для SetBrowserResponseStreamingEnabled значение false на HttpRequestMessage (requestMessage в следующем примере):
requestMessage.SetBrowserResponseStreamingEnabled(false);
Для получения дополнительной информации см. HttpClient и HttpRequestMessage с параметрами запроса Fetch API (статья Call web API).
Фингерпринтинг на стороне клиента
В выпуске .NET 9 было введено серверное определение отпечатков статических ресурсов в Blazor Web App, а также представлены соглашения маршрутизации конечных точек Map Static Assets (MapStaticAssets), компонент ImportMap и свойство ComponentBase.Assets (@Assets["..."]), для разрешения идентифицированных JavaScript-модулей (JS). Для .NET 10 можно выбрать использование клиентской дактилоскопии для модулей JS в автономных приложениях Blazor WebAssembly.
В автономных Blazor WebAssembly приложениях во время сборки и публикации фреймворк переопределяет заполнители в index.html значениями, вычисленными при сборке, для создания отпечатков пальцев статических ресурсов. Отпечаток пальца вставляется в имя файла скрипта blazor.webassembly.js.
Следующая разметка должна присутствовать в wwwroot/index.html файле для внедрения функции отпечатков пальцев:
<head>
...
+ <script type="importmap"></script>
</head>
<body>
...
- <script src="_framework/blazor.webassembly.js"></script>
+ <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>
</html>
В файл проекта (.csproj) добавьте свойство <OverrideHtmlAssetPlaceholders>, установленное на значение true.
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
+ <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
</Project>
В следующем примере все файлы, предоставленные разработчиком, являются модулями с расширением JS.
Модуль с именем scripts.js в папке приложения wwwroot/js идентифицируется путем добавления #[.{fingerprint}] перед расширением файла (.js):
<script type="module" src="js/scripts#[.{fingerprint}].js"></script>
Укажите выражение отпечатка пальца со <StaticWebAssetFingerprintPattern> свойством в файле проекта приложения (.csproj):
<ItemGroup>
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.js"
Expression="#[.{fingerprint}]!" />
</ItemGroup>
Любой JS файл (*.js) с маркером отпечатков пальцев в index.html, обрабатывается платформой, в том числе при публикации приложения.
Если вы используете .mjs расширение файла для JS модулей, задайте расширение файла с параметром Pattern :
<ItemGroup>
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs"
Expression="#[.{fingerprint}]!" />
</ItemGroup>
Файлы помещаются в карту импорта:
- Автоматически для Blazor Web App отрисовки на стороне клиента (CSR).
- При подключении функции идентификации модуля в самостоятельных Blazor WebAssembly приложениях в соответствии с приведенными выше инструкциями.
При разрешении импорта для взаимодействия с JavaScript карта импорта используется браузером для разрешения файлов с отпечатками.
Предварительно загруженные статические ресурсы Blazor фреймворка
Статические Blazor Web Appресурсы платформы автоматически загружаются с помощью Link заголовков, что позволяет браузеру предварительно загружать ресурсы до получения и отображения начальной страницы.
В отдельно работающих Blazor WebAssembly приложениях ресурсы платформы предназначены для высокоприоритетного скачивания и кэширования на ранних этапах index.html обработки страниц браузера.
Свойство
OverrideHtmlAssetPlaceholdersMSBuild в файле проекта приложения (.csproj) имеет значениеtrue:<PropertyGroup> <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders> </PropertyGroup>Следующий элемент
<link>, который содержитrel="preload", присутствует в содержимом<head>wwwroot/index.html:<link rel="preload" id="webassembly" />
Дополнительные сведения см. в разделе ASP.NET Core Blazor статических файлов.
Настройка среды в автономных Blazor WebAssembly приложениях
Заголовок Blazor-Environment и Properties/launchSettings.json файл (ASPNETCORE_ENVIRONMENT переменная среды) больше не используются для управления средой в автономных Blazor WebAssembly приложениях.
Начиная с .NET 10, задайте среду с помощью свойства <WasmApplicationEnvironmentName> в файле проекта приложения (.csproj).
В следующем примере среда приложения устанавливается: Staging.
<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
Среды по умолчанию:
-
Developmentдля сборки. -
Productionдля публикации.
Дополнительные сведения см. в разделе ASP.NET Core Blazor среды.
Файл конфигурации загрузки встроен
конфигурация загрузки Blazor, которая до выпуска .NET 10 существовала в файле с именем blazor.boot.json, была вложена в скрипт dotnet.js. Это влияет только на разработчиков, которые взаимодействуют непосредственно с файлом blazor.boot.json , например, когда разработчики:
- Проверка целостности файлов для опубликованных ресурсов с помощью скрипта PowerShell для устранения неисправностей проверки целостности в ASP.NET Core Blazor WebAssembly ошибках проверки целостности.
- Изменение расширения имени файла DLL при неналичии использования формата файла Webcil по умолчанию в соответствии с руководством "Хостинг и развертывание ASP.NET Core"
.
В настоящее время нет документированных стратегий замены для предыдущих подходов. Если вам требуется любая из предыдущих стратегий, откройте новую проблему документации, описывающую сценарий, используя ссылку "Открыть ссылку на проблему документации " в нижней части любой статьи.
Декларативная модель для консервирования состояния компонентов и сервисов
Теперь можно декларативно указать состояние для сохранения в компонентах и службах, используя атрибут [PersistentState]. Общедоступные (public) свойства с этим атрибутом автоматически сохраняются с помощью PersistentComponentState сервиса во время предварительного рендеринга. Состояние извлекается при интерактивном рендеринге компонента или при создании экземпляра службы.
В предыдущих Blazor выпусках сохранение состояния компонента во время предварительного рендеринга с помощью PersistentComponentState сервиса требовало большого объема кода, как показано в следующем примере:
@page "/movies"
@implements IDisposable
@inject IMovieService MovieService
@inject PersistentComponentState ApplicationState
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
public List<Movie>? MoviesList { get; set; }
private PersistingComponentStateSubscription? persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<List<Movie>>(nameof(MoviesList),
out var movies))
{
MoviesList = await MovieService.GetMoviesAsync();
}
else
{
MoviesList = movies;
}
persistingSubscription = ApplicationState.RegisterOnPersisting(() =>
{
ApplicationState.PersistAsJson(nameof(MoviesList), MoviesList);
return Task.CompletedTask;
});
}
public void Dispose() => persistingSubscription?.Dispose();
}
Теперь этот код можно упростить с помощью новой декларативной модели:
@page "/movies"
@inject IMovieService MovieService
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
[PersistentState]
public List<Movie>? MoviesList { get; set; }
protected override async Task OnInitializedAsync()
{
MoviesList ??= await MovieService.GetMoviesAsync();
}
}
Используйте public свойства, так как отражение используется платформой для таких задач, как обрезка неиспользуемого кода и создания источника.
Состояние можно сериализовать для нескольких компонентов одного типа, и вы можете установить декларативное состояние в службе для использования в приложении, вызвав RegisterPersistentService построитель компонентов Razor (AddRazorComponents) с пользовательским типом службы и режимом отрисовки. Дополнительные сведения см. в разделе ASP.NET Core Blazor сохраняемость предопределенного состояния.
Новые функции взаимодействия JavaScript
Blazor добавляет поддержку следующих JS функций взаимодействия:
- Создайте экземпляр объекта JS с помощью функции конструктора и получите дескриптор IJSObjectReference/IJSInProcessObjectReference .NET для ссылки на экземпляр.
- Чтение или изменение значения свойства объекта JS, как для свойств данных, так и для аксессорных свойств.
Следующие асинхронные методы доступны на IJSRuntime и IJSObjectReference с таким же поведением области видимости, как и у существующего метода IJSRuntime.InvokeAsync:
InvokeConstructorAsync(string identifier, object?[]? args): вызывает указанную JS функцию конструктора асинхронно. Функция вызывается операторомnew. В следующем примере,jsInterop.TestClass— это класс с конструктором, аclassRef— IJSObjectReference.var classRef = await JSRuntime.InvokeConstructorAsync("jsInterop.TestClass", "Blazor!"); var text = await classRef.GetValueAsync<string>("text"); var textLength = await classRef.InvokeAsync<int>("getTextLength");GetValueAsync<TValue>(string identifier): считывает значение указанного JS свойства асинхронно. Свойство не может быть свойствомset-only. Исключение JSException выбрасывается, если свойство не существует. В следующем примере возвращается значение из свойства данных:var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>( "jsInterop.testObject.num");SetValueAsync<TValue>(string identifier, TValue value): обновляет значение указанного JS свойства асинхронно. Свойство не может быть свойствомget-only. Если свойство не определено в целевом объекте, создается свойство. Генерируется JSException, если свойство существует, но не может быть записано, или если новое свойство не может быть добавлено в объект. В следующем примереnumсоздается наtestObjectсо значением 30, если оно не существует:await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
Перегрузки доступны для каждого из предыдущих методов, принимающих аргумент CancellationToken или аргумент времени ожидания TimeSpan.
Следующие синхронные методы доступны на IJSInProcessRuntime и IJSInProcessObjectReference с тем же поведением области, что и существующий метод IJSInProcessObjectReference.Invoke:
InvokeConstructor(string identifier, object?[]? args): синхронно вызывает указанную JS функцию конструктора. Функция вызывается операторомnew. В следующем примере,jsInterop.TestClass— это класс с конструктором, аclassRef— IJSInProcessObjectReference.var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var classRef = inProcRuntime.InvokeConstructor("jsInterop.TestClass", "Blazor!"); var text = classRef.GetValue<string>("text"); var textLength = classRef.Invoke<int>("getTextLength");GetValue<TValue>(string identifier): считывает значение указанного JS свойства синхронно. Свойство не может быть свойствомset-only. Исключение JSException выбрасывается, если свойство не существует. В следующем примере возвращается значение из свойства данных:var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var valueFromDataProperty = inProcRuntime.GetValue<int>( "jsInterop.testObject.num");SetValue<TValue>(string identifier, TValue value): обновляет значение указанного JS свойства синхронно. Свойство не может быть свойствомget-only. Если свойство не определено в целевом объекте, создается свойство. Генерируется JSException, если свойство существует, но не может быть записано, или если новое свойство не может быть добавлено в объект. В следующем примере, еслиnumне существует, он создается наtestObjectсо значением 20:var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); inProcRuntime.SetValue("jsInterop.testObject.num", 20);
Дополнительные сведения см. в следующих разделах статьи Вызов JavaScript-функций из методов .NET:
- Создание экземпляра JS объекта с помощью функции конструктора
- Чтение или изменение значения JS свойства объекта
Blazor WebAssembly счетчики профилирования производительности и диагностики
Новые счетчики профилирования производительности и диагностики доступны для Blazor WebAssembly приложений. Дополнительные сведения см. в следующих статьях:
- ASP.NET Core Blazor WebAssembly средства диагностики для разработчика браузера
- ASP.NET Core Blazor WebAssembly Диагностика канала событий
Выберите избегание NavigationException при статической серверной отрисовке с NavigationManager.NavigateTo
Вызов NavigationManager.NavigateTo во время статической серверной отрисовки (SSR) вызывает NavigationException прерывание выполнения перед преобразованием в ответ перенаправления. Это может привести к путанице во время отладки и не соответствует интерактивному поведению визуализации, где код после NavigateTo продолжает выполняться нормально.
В .NET 10 можно задать для свойства <BlazorDisableThrowNavigationException> MSBuild значение true в файле проекта приложения, чтобы избежать возникновения исключения во время статического SSR:
<PropertyGroup>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>
При использовании набора свойств MSBuild вызов NavigationManager.NavigateTo во время статического SSR больше не приводит к возникновению NavigationException исключения. Вместо этого он ведет себя согласованно с интерактивной прорисовкой, осуществляя навигацию без генерации исключений. Код после NavigationManager.NavigateTo выполняется перед перенаправлением.
Шаблон проекта .NET 10 Blazor Web App задает для свойства MSBuild значение true по умолчанию. Рекомендуем, чтобы при обновлении приложений до .NET 10 вы использовали новое свойство MSBuild и избегали предыдущего поведения.
Если используется свойство MSBuild, необходимо обновить код, который зависит от выбрасывания NavigationException. По умолчанию в пользовательском интерфейсе шаблона проекта Blazor до выпуска .NET 10, Identity выбрасывает Blazor Web App после вызова IdentityRedirectManager, чтобы подтвердить, что метод не был вызван во время интерактивной отрисовки. Это исключение и [DoesNotReturn] атрибуты теперь должны быть удалены при использовании свойства MSBuild. Дополнительные сведения см. в разделе Миграция с ASP.NET Core в .NET 9 на ASP.NET Core в .NET 10.
Blazor Маршрутизатор имеет NotFoundPage параметр
Blazor теперь предоставляет улучшенный способ отображения страницы "Не найден" при переходе на несуществующую страницу. Вы можете указать страницу для рендеринга, когда NavigationManager.NotFound (описано в следующем разделе) вызывается, передав тип страницы компоненту Router с помощью параметра NotFoundPage. Эта функция поддерживает маршрутизацию, работает в промежуточных слоях страниц кода состояния и совместима даже с неBlazor сценариями.
Фрагмент NotFound отрисовки (<NotFound>...</NotFound>) не поддерживается в .NET 10 или более поздних версиях.
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>This content is ignored because NotFoundPage is defined.</NotFound>
</Router>
Шаблон Blazor проекта теперь включает страницу NotFound.razor по умолчанию. Эта страница автоматически отрисовывается каждый раз, когда вызывается NotFound в вашем приложении, что упрощает обработку отсутствующих маршрутов, обеспечивая согласованный пользовательский опыт.
Дополнительные сведения см. в разделе ASP.NET Core Blazor навигации.
Не найдено ответов, использующих NavigationManager статический SSR и глобальную интерактивную отрисовку
Теперь NavigationManager включает метод NotFound для обработки сценариев, когда запрошенный ресурс не найден во время серверного SSR или глобальной интерактивной отрисовки.
Статическая серверная отрисовка (static SSR): вызов NotFound устанавливает код состояния HTTP на 404.
Интерактивная отрисовка: сигнализирует Blazor маршрутизатору (
Routerкомпоненту) для отрисовки не найденного содержимого.Потоковая отрисовка: если расширенная навигация активна, потоковая отрисовка отрисовывает не найденное содержимое без перезагрузки страницы. Если расширенная навигация заблокирована, платформа перенаправляется на содержимое Not Found с обновлением страницы.
Потоковая отрисовка может отображать только компоненты, имеющие маршрут, например NotFoundPage назначение (NotFoundPage="...") или назначение страницы повторного выполнения по коду состояния промежуточного слоя (UseStatusCodePagesWithReExecute).
DefaultNotFound 404 содержимое ("Not foundобычный текст") не имеет маршрута, поэтому его нельзя использовать во время потоковой отрисовки.
NotFound Рендеринг содержимого использует следующее, независимо от того, начался ли ответ (в порядке):
- Если NotFoundEventArgs.Path задан, отобразите содержимое назначенной страницы.
- Если
Router.NotFoundPageзадан, отобразите назначенную страницу. - При правильной настройке страниц кодов состояния через промежуточное программное обеспечение повторного выполнения.
- Никаких действий, если ни один из предыдущих подходов не принят.
ПО переобработки страниц с кодами состояния имеет UseStatusCodePagesWithReExecute приоритет в решении проблем маршрутизации адресов, связанных с браузером, таких как ввод неправильного URL-адреса в адресную строку браузера или выбор ссылки, не ведущей к конечной точке в приложении.
Событие NavigationManager.OnNotFound можно использовать для уведомлений при вызове NotFound.
Дополнительные сведения и примеры см. в разделе ASP.NET Core Blazor навигации.
Поддержка ответов о ненахождении в приложениях без Blazor маршрутизатора
Приложения, реализующие настраиваемый маршрутизатор, могут использовать NotFound. Существует два способа сообщить отрисовщику, какую страницу следует отобразить при NotFound вызове.
Рекомендуемый подход, который работает независимо от состояния ответа, заключается в вызове UseStatusCodePagesWithReExecute. При вызове NotFound промежуточное ПО отрисовывает путь, переданный методу.
app.UseStatusCodePagesWithReExecute(
"/not-found", createScopeForStatusCodePages: true);
Если вы не хотите использовать UseStatusCodePagesWithReExecute, приложение всё равно может поддерживать NotFound для ответов, уже начатых. Подпишитесь на OnNotFoundEvent в маршрутизаторе и назначьте путь страницы "Not Found" к NotFoundEventArgs.Path, чтобы сообщить отрисовщику, какое содержимое необходимо отобразить, когда вызывается NotFound.
CustomRouter.razor:
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Http
@implements IDisposable
@inject NavigationManager NavigationManager
@code {
protected override void OnInitialized() =>
NavigationManager.OnNotFound += OnNotFoundEvent;
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
{
// Only execute the logic if HTTP response has started
// because setting NotFoundEventArgs.Path blocks re-execution
if (HttpContext?.Response.HasStarted == false)
{
return;
}
e.Path = GetNotFoundRoutePath();
}
// Return the path of the Not Found page that you want to display
private string GetNotFoundRoutePath()
{
...
}
public void Dispose() => NavigationManager.OnNotFound -= OnNotFoundEvent;
}
При использовании обоих подходов в приложении путь "Не найден", указанный в OnNotFoundEvent обработчике, имеет приоритет над путем, настроенным в промежуточном слое повторного исполнения.
Метрики и трассировка
В этом выпуске представлены полные метрики и функции трассировки для Blazor приложений, обеспечивающие подробную наблюдаемость жизненного цикла компонентов, навигации, обработки событий и управления схемами.
Дополнительные сведения см. в разделе ASP.NET Core Blazor рекомендации по производительности.
Поддержка пакета JavaScript
BlazorВыходные данные сборки несовместимы с пакетами JavaScript, такими как Gulp, Webpack и Rollup.
Blazor теперь может создавать совместимые с бандлерами выходные данные при публикации, установив свойство MSBuild в WasmBundlerFriendlyBootConfig.
Дополнительные сведения см. в разделе Host и развертывание ASP.NET Core Blazor.
Blazor WebAssembly Предварительная загрузка статических ресурсов в Blazor Web App файлах
Мы заменили заголовки <link> на компонент ResourcePreloader (<ResourcePreloader />) для предварительной загрузки ассетов WebAssembly в Blazor Web Appах. Это позволяет конфигурации базового пути приложения (<base href="..." />) правильно определить корневой каталог приложения.
Удаление компонента отключает функцию, если приложение использует обратныйloadBootResource вызов для изменения URL-адресов.
Шаблон Blazor Web App по умолчанию принимает функцию в .NET 10, а приложения, обновляющиеся до .NET 10, могут реализовать эту функцию, разместив компонент ResourcePreloader после базового тега URL-адреса (<base>) в содержимом головного компонента App (App.razor):
<head>
...
<base href="/" />
+ <ResourcePreloader />
...
</head>
Дополнительные сведения см. в статье Хостинг и развертывание серверных приложений ASP.NET CoreBlazor.
Улучшенная проверка формы
Blazor Теперь улучшены возможности проверки формы, включая поддержку проверки свойств вложенных объектов и элементов коллекции.
Чтобы создать проверенную форму, используйте DataAnnotationsValidator компонент внутри EditForm компонента, как и раньше.
Чтобы выбрать новую функцию проверки, выполните следующие действия.
- Вызовите метод расширения
AddValidationв файлеProgram, где зарегистрированы службы. - Объявите типы моделей форм в файле класса C#, а не в компоненте Razor (
.razor). - Заметите тип модели корневой формы атрибутом
[ValidatableType].
Без выполнения предыдущих шагов поведение проверки остается таким же, как и в предыдущих .NET выпусках.
В следующем примере показаны заказы клиентов с улучшенной проверкой формы (сведения, пропущенные для краткости):
Чтобы включить новое поведение проверки, вызовите Program.cs в коллекции служб AddValidation.
builder.Services.AddValidation();
В следующем Order классе [ValidatableType] атрибут требуется для типа модели верхнего уровня. Другие типы обнаруживаются автоматически.
OrderItem и ShippingAddress не отображаются для краткости, но проверка вложенных и коллекций работает так же, как и в этих типах, если бы они были показаны.
Order.cs:
[ValidatableType]
public class Order
{
public Customer Customer { get; set; } = new();
public List<OrderItem> OrderItems { get; set; } = [];
}
public class Customer
{
[Required(ErrorMessage = "Name is required.")]
public string? FullName { get; set; }
[Required(ErrorMessage = "Email is required.")]
public string? Email { get; set; }
public ShippingAddress ShippingAddress { get; set; } = new();
}
В следующем компоненте OrderPage компонент DataAnnotationsValidator присутствует в компоненте EditForm.
OrderPage.razor:
<EditForm Model="Model">
<DataAnnotationsValidator />
<h3>Customer Details</h3>
<div class="mb-3">
<label>
Full Name
<InputText @bind-Value="Model!.Customer.FullName" />
</label>
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</div>
@* ... form continues ... *@
</EditForm>
@code {
public Order? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
// ... code continues ...
}
Требование объявить типы моделей за пределами Razor компонентов (.razor файлов) обусловлено тем, что как новая функция проверки, так и Razor сам компилятор используют генератор источника. В настоящее время выходные данные одного генератора источника нельзя использовать в качестве входных данных для другого генератора источника.
Теперь поддержка проверки включает:
- Теперь поддерживается проверка вложенных сложных объектов и коллекций.
- Сюда входят правила проверки, определенные атрибутами свойств, атрибутами класса и реализацией IValidatableObject .
- Атрибут
[SkipValidation]может исключить свойства или типы из проверки.
- Теперь проверка использует реализацию на основе генератора исходного кода вместо отражательной реализации для повышения производительности и совместимости с ранней компиляцией (AOT).
Теперь компонент DataAnnotationsValidator имеет тот же порядок проверки и короткое замыкание, что и System.ComponentModel.DataAnnotations.Validator. При проверке экземпляра типа Tприменяются следующие правила:
-
TСвойства элементов проверяются, включая рекурсивную проверку вложенных объектов. - Проверяются атрибуты на уровне типа
T. - Метод IValidatableObject.Validate выполняется, если
Tреализует его.
Если один из предыдущих шагов создает ошибку проверки, остальные шаги пропускаются.
Используйте модели проверки из другой сборки
Формы можно проверить, используя модели, которые определены в другой сборке, например в библиотеке или проекте .ClientBlazor Web App, путем создания метода в библиотеке или проекте .ClientBlazor Web App, который принимает AddValidation экземпляр в качестве аргумента и вызывает на нём .
- В приложении вызовите как метод, так и
AddValidation.
Дополнительные сведения и пример см. в разделе ASP.NET Core Blazor проверки форм.
Пользовательский Blazor кэш и BlazorCacheBootResources свойство MSBuild удалены
Теперь, когда все Blazor клиентские файлы имеют уникальные отпечатки и кэшируются браузером, пользовательский механизм кэширования Blazor и свойство MSBuild BlazorCacheBootResources были удалены из фреймворка. Если файл проекта на стороне клиента содержит свойство MSBuild, удалите это свойство, так как оно больше не имеет никакого эффекта:
- <BlazorCacheBootResources>...</BlazorCacheBootResources>
Дополнительные сведения см. в разделе ASP.NET Core Blazor WebAssembly сбои кэширования и проверки целостности.
Поддержка API веб-проверки подлинности (passkey) для ASP.NET Core Identity
Поддержка API веб-проверки подлинности (WebAuthn), широко известная как ключи доступа, — это современный метод проверки подлинности, устойчивый к фишингу, который повышает безопасность и улучшает взаимодействие с пользователем, используя криптографию открытого ключа и проверку подлинности на основе устройств. ASP.NET Core Identity теперь поддерживает аутентификацию с использованием паролей на основе стандартов WebAuthn и FIDO2. Эта функция позволяет пользователям входить без паролей, используя безопасные методы проверки подлинности на основе устройств, такие как биометрические данные или ключи безопасности.
Шаблон Blazor Web App проекта предоставляет встроенные функции управления ключами и входа.
Дополнительные сведения см. в следующих статьях:
- Включение секретных ключей API веб-проверки подлинности (WebAuthn)
- Реализация passkeys в ASP.NET Core Blazor Web Apps
Сохраняемость состояния канала
Во время серверной отрисовки Blazor Web App теперь пользователи могут сохранять состояние сеанса (цирк) при длительном отключении соединения с сервером или намеренной приостановке, если не выполняется полное обновление страницы. Это позволяет пользователям возобновлять сеанс без потери несохраненных работ в следующих сценариях:
- Регулирование вкладок браузера
- Переключение приложений для мобильных устройств
- Прерывания сети
- Упреждающее управление ресурсами (приостановка неактивных каналов)
- Улучшенная навигация
Дополнительные сведения см. в разделе ASP.NET Core Blazor серверное управление состоянием.
Hot Reload для Blazor WebAssembly и .NET в WebAssembly
Пакет SDK был перенесен на универсальную версию Hot Reload для сценариев WebAssembly. Существует новое свойство MSBuild WasmEnableHotReload, которое true по умолчанию для конфигурации Debug (Configuration == "Debug"), которая включает Hot Reload.
Для других конфигураций с пользовательскими именами конфигураций задайте значение true в файле проекта приложения, чтобы включить Hot Reload:
<PropertyGroup>
<WasmEnableHotReload>true</WasmEnableHotReload>
</PropertyGroup>
Чтобы отключить Hot Reload для конфигурации Debug, задайте значение false:
<PropertyGroup>
<WasmEnableHotReload>false</WasmEnableHotReload>
</PropertyGroup>
Обновлена регистрация служебного работника PWA, чтобы предотвратить проблемы с кэшированием
Регистрация сервисного работника в шаблоне проекта прогрессивногоBlazor веб-приложения (PWA) теперь включает updateViaCache: 'none' параметр, который предотвращает проблемы с кэшированием во время обновления сервисного работника.
- navigator.serviceWorker.register('service-worker.js');
+ navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' });
Этот параметр гарантирует следующее:
- Браузер не использует кэшированные версии скрипта сервисного работника.
- Обновления Service Worker применяются надежно и не блокируются кэшированием HTTP.
- Приложения PWA могут обновлять свои рабочие службы более предсказуемо.
Это устраняет проблемы с кэшированием, которые могут препятствовать правильному применению обновлений Service Worker, что особенно важно для PWA, которые полагаются на Service Worker для автономных функций.
Мы рекомендуем использовать параметр none во всех прогрессивных веб-приложениях (PWA), включая те, которые предназначены для .NET 9 или более ранних версий.
Расширяемость сериализации для состояния постоянного компонента
Реализуйте пользовательский сериализатор с помощью PersistentComponentStateSerializer<T>. Без зарегистрированного пользовательского сериализатора сериализация возвращается к существующей сериализации JSON.
Настраиваемый сериализатор регистрируется в файле приложения Program . В следующем примере CustomUserSerializer регистрируется для типа TUser:
builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>,
CustomUserSerializer>();
Тип автоматически сохраняется и восстанавливается с помощью пользовательского сериализатора:
[PersistentState]
public User? CurrentUser { get; set; } = new();
OwningComponentBase теперь внедряет IAsyncDisposable
OwningComponentBase теперь включает поддержку асинхронного удаления, улучшение управления ресурсами. Существуют новые методы DisposeAsync и DisposeAsyncCore, а также обновленный метод Dispose для обработки как синхронного, так и асинхронного освобождения области службы.
Новый InputHidden компонент для обработки скрытых полей ввода в формах
Новый InputHidden компонент предоставляет скрытое поле ввода для хранения строковых значений.
В следующем примере для свойства формы Parameter создается скрытое поле ввода. При отправке формы отображается значение скрытого поля:
<EditForm Model="Parameter" OnValidSubmit="Submit" FormName="InputHidden Example">
<InputHidden id="hidden" @bind-Value="Parameter" />
<button type="submit">Submit</button>
</EditForm>
@if (submitted)
{
<p>Hello @Parameter!</p>
}
@code {
private bool submitted;
[SupplyParameterFromForm]
public string Parameter { get; set; } = "stranger";
private void Submit() => submitted = true;
}
Поддержка постоянного состояния компонента для улучшенной навигации
Blazor теперь поддерживает обработку состояния постоянного компонента во время расширенной навигации. Состояние, сохраненное во время расширенной навигации, можно считывать интерактивными компонентами на странице.
По умолчанию состояние сохраняемого компонента загружается только интерактивными компонентами при первоначальной загрузке на странице. Это предотвращает перезапись важных состояний, таких как данные в редактируемой веб-форме, если дополнительные расширенные события навигации происходят на той же странице после загрузки компонента.
Если данные доступны только для чтения и часто не изменяются, вы можете разрешить обновления во время расширенной навигации, задав AllowUpdates = trueатрибут[PersistentState]. Это полезно для таких сценариев, как отображение кэшированных данных, которые очень дороги для получения, но часто не изменяются. В следующем примере показано использование данных прогноза AllowUpdates погоды:
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastAsync();
}
Чтобы пропустить восстановление состояния во время предварительной отрисовки, установите значение RestoreBehaviorSkipInitialValue:
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
Чтобы пропустить восстановление состояния во время повторного подключения, установите значение RestoreBehaviorSkipLastSnapshot. Это может быть полезно для обеспечения свежих данных после повторного подключения:
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
Вызов PersistentComponentState.RegisterOnRestoring позволит зарегистрировать обратный вызов для императивного управления восстановлением состояния, аналогично тому, как PersistentComponentState.RegisterOnPersisting предоставляет полный контроль над сохранением состояния.
Blazor WebAssembly учитывает текущую настройку культуры пользовательского интерфейса
В .NET 9 или более ранних версиях автономные Blazor WebAssembly приложения загружают ресурсы глобализации пользовательского интерфейса на базе CultureInfo.DefaultThreadCurrentCulture. Если вы хотите дополнительно загрузить данные о глобализации для вашей локализации, определенной CultureInfo.DefaultThreadCurrentUICulture, обновите приложение до .NET 10 или более поздней версии.
Blazor Hybrid
В этом разделе описываются новые функции для Blazor Hybrid.
Новые .NET MAUIBlazor Hybrid с Blazor Web App и ASP.NET Core Identity статья и пример
Добавлена новая статья и пример приложения для .NET MAUIBlazor Hybrid и веб-приложения с помощью ASP.NET Core Identity.
Дополнительные сведения см. в следующих ресурсах:
- .NET MAUI Blazor Hybrid и веб-приложение с ASP.NET Core Identity
-
MauiBlazorWebIdentityпример приложения (репозиторийdotnet/blazor-samplesGitHub)
SignalR
В этом разделе описываются новые функции для SignalR.
Минимальные API
В этом разделе описываются новые функции для минимальных API.
Обработка пустой строки при отправке формы как null для nullable типов значений
При использовании атрибута [FromForm] со сложным объектом в минимальных API пустые строковые значения в записи формы теперь преобразуются в null вместо того, чтобы вызвать ошибку синтаксического анализа. Это поведение соответствует логике обработки записей форм, не связанных с сложными объектами в минимальных API.
using Microsoft.AspNetCore.Http;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/todo", ([FromForm] Todo todo) => TypedResults.Ok(todo));
app.Run();
public class Todo
{
public int Id { get; set; }
public DateOnly? DueDate { get; set; } // Empty strings map to `null`
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
Благодаря @nvmkpk за вклад в это изменение!
Поддержка проверки в минимальных API
Теперь доступна поддержка проверки в минимальных API. Эта функция позволяет запрашивать проверку данных, отправленных конечным точкам API. Включение проверки позволяет среде выполнения ASP.NET Core выполнять любые проверки, определенные в следующих параметрах:
- Query
- Header
- Основное содержание запроса
Проверки определяются с помощью атрибутов в DataAnnotations пространстве имен. Разработчики настраивают поведение системы проверки следующим образом:
- Создание пользовательских реализаций атрибутов
[Validation]. - Реализация интерфейса
IValidatableObjectдля сложной логики проверки.
Если проверка завершается ошибкой, среда выполнения возвращает ответ 400 Неверный запрос с подробными сведениями об ошибках проверки.
Включение встроенной поддержки проверки для минимальных API
Включите встроенную поддержку проверки для Minimal APIs, вызвав метод расширения AddValidation, чтобы зарегистрировать необходимые службы в контейнере сервисов вашего приложения.
builder.Services.AddValidation();
Реализация автоматически обнаруживает типы, определенные в обработчиках Minimal API или как базовые типы для типов, определенных в обработчиках Minimal API. Фильтр конечной точки выполняет проверку этих типов и добавляется для каждой конечной точки.
Проверка может быть отключена для определенных конечных точек с помощью DisableValidation метода расширения, как показано в следующем примере:
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Note
Несколько небольших улучшений и исправлений были сделаны в генераторе проверки минимальных API, представленных в ASP.NET Core для .NET 10. Для поддержки будущих улучшений базовые API сопоставителя проверки теперь помечены как экспериментальные. API верхнего уровня AddValidation и встроенный фильтр проверки остаются стабильными и не экспериментальными.
Проверка корректности типов записей
Минимальные API также поддерживают проверку с помощью типов записей C#. Типы записей можно проверить с помощью атрибутов из System.ComponentModel.DataAnnotations пространства имен, аналогичных классам. Рассмотрим пример.
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
При использовании типов записей в качестве параметров в конечных точках Минимального API атрибуты проверки автоматически применяются так же, как и типы классов:
app.MapPost("/products", (Product product) =>
{
// Endpoint logic here
return TypedResults.Ok(product);
});
Минимальная интеграция проверки API с IProblemDetailsService
Ответы на ошибки логики валидации для минимальных API теперь можно настроить с помощью реализации IProblemDetailsService, предоставленной в коллекции служб приложения (контейнер внедрения зависимостей). Это обеспечивает более согласованные и ориентированные на пользователя ответы на ошибки.
Поддержка событий, отправляемых сервером (SSE)
ASP.NET Core теперь поддерживает возврат результата ServerSentEvents с помощью API TypedResults.ServerSentEvents. Эта функция поддерживается как в минимальных API, так и в приложениях на основе контроллера.
Server-Sent События — это технология серверной передачи, которая позволяет серверу отправлять поток сообщений о событиях клиенту по единому HTTP-соединению. В .NET сообщения о событиях представлены как объекты SseItem<T>, которые могут содержать тип события, идентификатор и полезные данные типа T.
Класс TypedResults имеет новый статический метод ServerSentEvents, который можно использовать для возврата результата ServerSentEvents. Первым параметром этого метода является IAsyncEnumerable<SseItem<T>> тот, который представляет поток сообщений о событиях, отправляемых клиенту.
В следующем примере показано, как использовать TypedResults.ServerSentEvents API для возврата потока событий пульса в качестве объектов JSON клиенту:
app.MapGet("/json-item", (CancellationToken cancellationToken) =>
{
async IAsyncEnumerable<HeartRateRecord> GetHeartRate(
[EnumeratorCancellation] CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var heartRate = Random.Shared.Next(60, 100);
yield return HeartRateRecord.Create(heartRate);
await Task.Delay(2000, cancellationToken);
}
}
return TypedResults.ServerSentEvents(GetHeartRate(cancellationToken),
eventType: "heartRate");
});
Дополнительные сведения можно найти здесь
- Server-Sent События на MDN.
-
Минимальное приложение-пример API с использованием
TypedResults.ServerSentEventsAPI для возврата потока событий сердечного ритма в виде строки и объектов JSON клиенту. - Приложение-пример API контроллера, использующее API для передачи потока событий пульса клиенту в виде строки и объектов JSON
TypedResults.ServerSentEvents.
API проверки перемещены в Microsoft. Extensions.Validation
API проверки перемещены в пространство имен Microsoft.Extensions.Validation и пакет NuGet. Это изменение делает API-интерфейсы доступными за пределами сценариев ASP.NET Core HTTP. Общедоступные API и поведение остаются неизменными— только пакеты и пространство имен отличаются. Существующие проекты не требуют изменений кода, так как старые ссылки перенаправляются в новую реализацию.
Расширенная проверка для классов и записей
Теперь атрибуты проверки можно применять как к классам, так и к записям с согласованным поведением создания кода и проверки. Это улучшение повышает гибкость при проектировании моделей с помощью записей в приложениях ASP.NET Core.
Вклад сообщества: благодаря @marcominerva!
OpenAPI
В этом разделе описываются новые возможности OpenAPI.
Поддержка OpenAPI 3.1
ASP.NET Core добавил поддержку создания документов OpenAPI версии 3.1 в .NET 10. Несмотря на незначительное увеличение версии, OpenAPI 3.1 является значительным обновлением спецификации OpenAPI, в частности с полной поддержкой черновика схемы JSON 2020-12.
Некоторые изменения, которые вы увидите в созданном документе OpenAPI, включают:
- Типы nullable больше не имеют свойства
nullable: trueв схеме. - Вместо свойства
nullable: trueони имеют ключевое словоtype, значение которого является массивом, который включаетnullв качестве одного из типов. - Свойства или параметры, определенные как C#
intилиlongтеперь отображаются в созданном документе OpenAPI безtype: integerполя и имеютpatternполе, ограничивающее значение цифрами. Это происходит, когда свойство NumberHandling в JsonSerializerOptions имеет значениеAllowReadingFromString, значение по умолчанию для веб-приложений ASP.NET Core. Чтобы C#intиlongмогли представляться в документе OpenAPI какtype: integer, установите для свойства NumberHandling значениеStrict.
При использовании этой функции версия OpenAPI по умолчанию для созданных документов3.1. Версию можно изменить, явно задав свойство OpenApiVersion объекта OpenApiOptions в configureOptions параметре делегата AddOpenApi:
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1;
});
Для выбора версии OpenAPI при создании документа во время сборки, установите --openapi-version в элементе OpenApiGenerateDocumentsOptions MSBuild.
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<!-- Configure build-time OpenAPI generation to produce an OpenAPI 3.1 document. -->
<OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_1</OpenApiGenerateDocumentsOptions>
</PropertyGroup>
Поддержка OpenAPI 3.1 была добавлена в первую очередь в следующем PR.
Изменения, нарушающие совместимость в OpenAPI 3.1
Для поддержки OpenAPI 3.1 требуется обновление базовой библиотеки OpenAPI.NET до новой основной версии 2.0. Эта новая версия содержит некоторые изменения, которые могут нарушить совместимость с предыдущей версией. Разрушающие изменения могут повлиять на приложения, если они используют трансформеры документов, операций или схем. Критические изменения в этой итерации включают следующие:
- Сущности в документе OpenAPI, такие как операции и параметры, являются интерфейсами. Конкретные реализации существуют как для встроенных, так и для ссылочных вариантов сущности. Например,
IOpenApiSchemaможет быть встроеннымOpenApiSchemaилиOpenApiSchemaReference, указывающим на схему, определенную в другом месте документа. - Свойство
Nullableбыло удалено из типаOpenApiSchema. Чтобы определить, является ли тип допускающим значение null, проверьте, устанавливает ли свойствоOpenApiSchema.TypeJsonSchemaType.Null.
Одним из наиболее значительных изменений является то, что класс OpenApiAny был удален в пользу использования JsonNode напрямую. Для использования OpenApiAnyнеобходимо обновить преобразователи, использующие JsonNode. В следующем диффе показаны изменения в преобразователе схемы от .NET 9 до .NET 10:
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(WeatherForecast))
{
- schema.Example = new OpenApiObject
+ schema.Example = new JsonObject
{
- ["date"] = new OpenApiString(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd")),
+ ["date"] = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"),
- ["temperatureC"] = new OpenApiInteger(0),
+ ["temperatureC"] = 0,
- ["temperatureF"] = new OpenApiInteger(32),
+ ["temperatureF"] = 32,
- ["summary"] = new OpenApiString("Bracing"),
+ ["summary"] = "Bracing",
};
}
return Task.CompletedTask;
});
Обратите внимание, что эти изменения необходимы даже при настройке только версии OpenAPI до версии 3.0.
OpenAPI в YAML
ASP.NET теперь поддерживает обслуживание созданного документа OpenAPI в формате YAML. YAML может быть более кратким, чем JSON, устраняя фигурные скобки и кавычки, когда их можно вывести. YAML также поддерживает многострочный формат строк, которые могут быть полезны для длинных описаний.
Чтобы настроить приложение для обслуживания созданного документа OpenAPI в формате YAML, укажите конечную точку в вызове MapOpenApi с суффиксом "yaml" или ".yml", как показано в следующем примере:
if (app.Environment.IsDevelopment())
{
app.MapOpenApi("/openapi/{documentName}.yaml");
}
Поддержка для:
- YAML в настоящее время доступен только для OpenAPI, обслуживаемого из конечной точки OpenAPI.
- Создание документов OpenAPI в формате YAML во время сборки будет добавлено в будущей предварительной версии.
См. этот PR, добавивший поддержку представления сгенерированного документа OpenAPI в формате YAML.
Описание отклика на ProducesResponseType для контроллеров API
Параметр ProducesAttribute, ProducesResponseTypeAttributeи ProducesDefaultResponseTypeAttribute теперь принимает необязательный строковый параметр, Descriptionкоторый задает описание ответа:
[HttpGet(Name = "GetWeatherForecast")]
[ProducesResponseType<IEnumerable<WeatherForecast>>(StatusCodes.Status200OK,
Description = "The weather forecast for the next 5 days.")]
public IEnumerable<WeatherForecast> Get()
{
Созданные данные OpenAPI:
"responses": {
"200": {
"description": "The weather forecast for the next 5 days.",
"content": {
Эта функция поддерживается как в контроллерах API, так и в минимальных API. Для минимальных API-интерфейсов свойство Description правильно задано, даже если тип атрибута и предполагаемый возвращаемый тип не совпадают.
Вклад сообщества (dotnet/aspnetcore No 58193)Сандер десять Бринк.
Перенос комментариев из XML-документа в документ OpenAPI
Создание документации OpenAPI в ASP.NET Core теперь будет включать метаданные из XML-комментариев к методам, классам и членам в документе OpenAPI. Чтобы использовать эту функцию, необходимо включить комментарии XML-документации в файле проекта. Это можно сделать, добавив в файл проекта следующее свойство:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Во время сборки пакет OpenAPI будет использовать генератор источника для обнаружения XML-комментариев в текущей сборке приложения и любых ссылок на проекты и отправки исходного кода для вставки в документ с помощью преобразователя документов OpenAPI.
Обратите внимание, что процесс сборки C# не фиксирует комментарии XML документации, размещенные в лямбда-выражениях, поэтому для добавления метаданных в минимальную конечную точку API необходимо определить обработчик конечной точки в виде метода, разместить XML комментарии документации в методе, а затем ссылаться на этот метод из метода MapXXX. Например, чтобы использовать примечания XML-документа для добавления метаданных в конечную точку Минимального API, изначально определяемую как лямбда-выражение:
app.MapGet("/hello", (string name) =>$"Hello, {name}!");
Измените вызов MapGet для ссылки на метод:
app.MapGet("/hello", Hello);
Определите метод Hello с помощью комментариев xml-документации:
static partial class Program
{
/// <summary>
/// Sends a greeting.
/// </summary>
/// <remarks>
/// Greeting a person by their name.
/// </remarks>
/// <param name="name">The name of the person to greet.</param>
/// <returns>A greeting.</returns>
public static string Hello(string name)
{
return $"Hello, {name}!";
}
}
В предыдущем примере метод Hello добавляется в класс Program, но его можно добавить в любой класс в проекте.
В предыдущем примере показаны <summary>, <remarks> и <param> XML-комментарии.
Дополнительные сведения о комментариях к XML-документам, включая все поддерживаемые теги, см. в документации по C#.
Так как основные функции предоставляются с помощью генератора источника, его можно отключить, добавив следующий MSBuild в файл проекта.
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.2.*" GeneratePathProperty="true" />
</ItemGroup>
<Target Name="DisableCompileTimeOpenApiXmlGenerator" BeforeTargets="CoreCompile">
<ItemGroup>
<Analyzer Remove="$(PkgMicrosoft_AspNetCore_OpenApi)/analyzers/dotnet/cs/Microsoft.AspNetCore.OpenApi.SourceGenerators.dll" />
</ItemGroup>
</Target>
Генератор исходного кода обрабатывает XML-файлы, включенные в свойство AdditionalFiles. Чтобы добавить (или удалить), источники изменяют свойство следующим образом:
<Target Name="AddXmlSources" BeforeTargets="CoreCompile">
<ItemGroup>
<AdditionalFiles Include="$(PkgSome_Package)/lib/net10.0/Some.Package.xml" />
</ItemGroup>
</Target>
Microsoft. AspNetCore.OpenApi добавлен в шаблон веб-API ASP.NET Core (native AOT)
Шаблон проекта ASP.NET Core веб-API (нативный AOT) (краткое имя webapiaot) теперь включает поддержку создания документов OpenAPI с помощью пакета Microsoft.AspNetCore.OpenApi по умолчанию. Эта поддержка отключена с помощью флага --no-openapi при создании нового проекта.
Вклад сообщества (dotnet/aspnetcore No 60337)Сандер десять Бринк.
Поддержка IOpenApiDocumentProvider в контейнере DI.
ASP.NET Core в .NET 10 поддерживает IOpenApiDocumentProvider в контейнере внедрения зависимостей (DI). Разработчики могут встраивать IOpenApiDocumentProvider в свои приложения и использовать его для доступа к документу OpenAPI. Этот подход полезен для доступа к документам OpenAPI вне контекста HTTP-запросов, таких как фоновые службы или пользовательское ПО промежуточного слоя.
Ранее для выполнения логики запуска приложения без запуска HTTP-сервера использовался HostFactoryResolver с реализацией IServer no-op. Новая функция упрощает этот процесс, предлагая оптимизированный API, вдохновленный Aspire и являющийся частью фреймворка IDistributedApplicationPublisher для размещения и публикации распределенных приложений.
Дополнительные сведения, см. в dotnet/aspnetcore #61463.
Усовершенствования генератора комментариев XML
Создание комментариев XML обрабатывает сложные типы в .NET 10 лучше, чем предыдущие версии .NET.
- Он создает точные и полные XML-комментарии для более широкого диапазона типов.
- Он обрабатывает более сложные сценарии.
- Она спокойно игнорирует обработку сложных типов, которые вызывают ошибки сборки в более ранних версиях.
Эти улучшения изменяют режим сбоя для определенных сценариев с ошибок сборки на отсутствие метаданных.
Кроме того, теперь можно настроить обработку комментариев XML для доступа к XML-комментариям в других сборках. Это полезно для создания документации по типам, определенным вне текущей сборки, например типу ProblemDetails в пространстве имен Microsoft.AspNetCore.Http.
Эта конфигурация выполняется с директивами в файле сборки проекта. В следующем примере показано, как настроить генератор комментариев XML для доступа к XML-комментариям для типов в сборке Microsoft.AspNetCore.Http, которая включает класс ProblemDetails.
<Target Name="AddOpenApiDependencies" AfterTargets="ResolveReferences">
<ItemGroup>
<!-- Include XML documentation from Microsoft.AspNetCore.Http.Abstractions
to get metadata for ProblemDetails -->
<AdditionalFiles
Include="@(ReferencePath->'
%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferencePath.Filename)' ==
'Microsoft.AspNetCore.Http.Abstractions'"
KeepMetadata="Identity;HintPath" />
</ItemGroup>
</Target>
Мы ожидаем включить XML-комментарии из выбранного набора сборок в общую платформу в будущих предварительных версиях, чтобы избежать необходимости в этой конфигурации в большинстве случаев.
Единая обработка идентификаторов документации в генераторе комментариев OpenAPI XML
Комментарии xml-документации из ссылочных сборок правильно объединяются, даже если идентификаторы документации включают суффиксы возвращаемого типа. В результате все допустимые XML-комментарии надежно включаются в создаваемую документацию OpenAPI, что улучшает точность и полноту документации для API, использующих ссылочные сборки.
Параметры перечисления данных формы используют фактический тип перечисления в OpenAPI
Параметры данных формы в действиях контроллера MVC теперь создают метаданные OpenAPI, используя фактический тип перечисления вместо строки.
Вклад сообщества: благодаря @ascott18!
Поддержка создания OpenApiSchemas в преобразователях
Теперь разработчики могут создать схему для типа C# с помощью той же логики, что используется для генерации документов OpenAPI в ASP.NET Core, и добавлять её в документ OpenAPI. Затем на схему можно ссылаться из других частей документа OpenAPI.
Контекст, передаваемый в документ, операции и преобразователи схем, включает новый GetOrCreateSchemaAsync метод, который можно использовать для создания схемы для типа.
Этот метод также имеет необязательный ApiParameterDescription параметр, чтобы указать дополнительные метаданные для созданной схемы.
Для поддержки добавления схемы в документ OpenAPI, свойство Document было добавлено в преобразователь операций и схем. Это позволяет любому преобразователю добавлять схему в документ OpenAPI с помощью метода документа AddComponent .
Example
Чтобы использовать эту функцию в документе, операции или преобразователе схемы, создайте схему с помощью GetOrCreateSchemaAsync метода, предоставленного в контексте, и добавьте его в документ OpenAPI с помощью метода документа AddComponent .
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer(async (operation, context, cancellationToken) =>
{
// Generate schema for error responses
var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
context.Document?.AddComponent("Error", errorSchema);
operation.Responses ??= new OpenApiResponses();
// Add a "4XX" response to the operation with the newly created schema
operation.Responses["4XX"] = new OpenApiResponse
{
Description = "Bad Request",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/problem+json"] = new OpenApiMediaType
{
Schema = new OpenApiSchemaReference("Error", context.Document)
}
}
};
});
});
Преобразователи операций OpenAPI для конкретной конечной точки
Преобразователи операций, относящиеся к конечной точке, позволяют точно настроить документацию OpenAPI для отдельных конечных точек маршрута. Эта функция позволяет разработчикам настраивать метаданные и описания Swagger/OpenAPI для каждого действия или на основе маршрутов, повышая расширяемость для расширенных сценариев API.
Сведения о реализации и примеры кода см. в разделе "Настройка документов OpenAPI".
Обновление Microsoft. OpenApi до 2.0.0
Библиотека Microsoft.OpenApi, используемая для создания документов OpenAPI в ASP.NET Core, была обновлена до версии 2.0.0 (GA).
Критические изменения в версии 2.0.0
Следующие критические изменения были введены в предварительных версиях и остаются в общедоступной версии. Они в первую очередь влияют на пользователей, реализующих преобразователи документов, операций или схем:
- Свойства эфемерного объекта теперь находятся в метаданных.
- Использовать объект метода HTTP вместо enum
При обновлении до общедоступной версии дальнейшие обратимые изменения не ожидаются в генерации документов OpenAPI.
Усовершенствования создания схемы OpenAPI
Моделируйте типы, допускающие значение NULL, используя oneOf в схеме OpenAPI.
Создание схемы OpenAPI для типов, допускающих значение NULL, было улучшено с помощью oneOf шаблона вместо свойства NULL для сложных типов и коллекций. Реализация:
- Используется
oneOfсnullи фактическая схема типа для сложных типов, допускающих значение NULL, в схемах запросов и ответов. - Обнаруживает значение NULL для параметров, свойств и возвращаемых типов с помощью отражения и
NullabilityInfoContext. - Удаляет типы NULL из компонентных схем, чтобы избежать дублирования.
Исправления и улучшения разрешения ссылок на схему
В этом выпуске улучшена обработка схем JSON для создания документов OpenAPI путем правильного разрешения относительных ссылок на схемы JSON ($ref) в корневом документе схемы.
Включение описаний свойств как братьев и сестер $ref в схеме OpenAPI
До выпуска .NET 10, ASP.NET Core удалял описания свойств, определенных с помощью $ref в созданном документе OpenAPI, поскольку OpenAPI версии 3.0 не допускал наличие родственных свойств одновременно с $ref в определениях схем. OpenAPI 3.1 теперь позволяет включать описания наряду с $ref. RC1 добавляет поддержку включения описаний свойств на одном уровне с $ref в созданную схему OpenAPI.
Это был вклад сообщества. Спасибо @desjoerd!
Добавление метаданных из комментариев XML для [AsParameters] типов в схему OpenAPI
Генерация схемы OpenAPI теперь обрабатывает XML-комментарии к свойствам классов параметров [AsParameters], для того чтобы извлечь метаданные для документации.
Исключение неизвестных методов HTTP из OpenAPI
Создание схемы OpenAPI теперь исключает неизвестные методы HTTP из созданного документа OpenAPI. Методы запросов, которые являются стандартными методами HTTP, но не распознаваемыми OpenAPI, теперь полностью исключены из созданного документа OpenAPI.
Это был вклад сообщества. Спасибо @martincostello!
Улучшение описания компонентов запросов JSON Patch
Создание схемы OpenAPI для операций JSON Patch теперь правильно применяет application/json-patch+json тип мультимедиа к телам запросов, использующим JSON Patch. Это гарантирует, что созданный документ OpenAPI точно отражает ожидаемый тип медиа для операций JSON Patch. Кроме того, текст запроса исправления JSON содержит подробную схему, описывающую структуру документа исправления JSON, включая операции, которые можно выполнить.
Это был вклад сообщества. Спасибо @martincostello!
Используйте инвариантную культуру для создания документов OpenAPI
Создание документов OpenAPI теперь использует инвариантную культуру для форматирования чисел и дат в созданном документе OpenAPI. Это гарантирует, что созданный документ будет согласованным и не будет зависеть от культурных настроек сервера.
Это был вклад сообщества. Спасибо @martincostello!
Проверка подлинности и авторизация
Метрики проверки подлинности и авторизации
Метрики добавлены для определенных событий проверки подлинности и авторизации в ASP.NET Core. С помощью этого изменения теперь можно получить метрики для следующих событий:
- Authentication:
- Длительность аутентифицированного запроса
- Количество испытаний
- Запретить подсчет
- Количество авторизаций
- Количество выходов из системы
- Authorization:
- Количество запросов, требующих авторизации
На следующем рисунке показан пример метрики длительности запроса с проверкой подлинности на Aspire панели мониторинга:
Дополнительные сведения см. в разделе ASP.NET Core встроенные метрики.
метрики ASP.NET Core Identity
ASP.NET Core Identity наблюдаемость была улучшена в .NET 10 с использованием метрик. Метрики — это счетчики, гистограммы и датчики, обеспечивающие измерения временных рядов системного поведения или поведения приложений.
Например, используйте новые метрики ASP.NET Core Identity для наблюдения:
- Управление пользователями: новые создания пользователей, изменения пароля и назначения ролей.
- Обработка входа и сеанса: попытки входа, входы, выходы и пользователи с помощью двухфакторной проверки подлинности.
Новые метрики находятся в счетчике Microsoft.AspNetCore.Identity:
aspnetcore.identity.user.create.durationaspnetcore.identity.user.update.durationaspnetcore.identity.user.delete.durationaspnetcore.identity.user.check_password_attemptsaspnetcore.identity.user.generated_tokensaspnetcore.identity.user.verify_token_attemptsaspnetcore.identity.sign_in.authenticate.durationaspnetcore.identity.sign_in.check_password_attemptsaspnetcore.identity.sign_in.sign_insaspnetcore.identity.sign_in.sign_outsaspnetcore.identity.sign_in.two_factor_clients_rememberedaspnetcore.identity.sign_in.two_factor_clients_forgotten
Дополнительные сведения об использовании метрик в ASP.NET Core см. в разделе ASP.NET Core метрики.
Избегайте cookie перенаправлений входа для известных конечных точек API
По умолчанию не аутентифицированные и неавторизованные запросы, сделанные к известным конечным точкам API, защищенным проверкой cookie подлинности, теперь приводят к ответам 401 и 403, а не к перенаправлению на URI для входа или отказа в доступе.
Это изменение было очень запрошено, так как перенаправление неавторентированных запросов на страницу входа обычно не имеет смысла для конечных точек API, которые обычно используют коды состояния 401 и 403, а не перенаправления HTML для обмена ошибками проверки подлинности.
Известные конечные точки API определяются с помощью нового IApiEndpointMetadata интерфейса, а метаданные, реализующие новый интерфейс, автоматически добавляются в следующее:
-
[ApiController]Конечные точки - Минимальные конечные точки API, которые обрабатывают тела запросов, содержащих JSON, или формируют ответы в формате JSON.
- Конечные точки, использующие типы возвращаемых данных
TypedResults - SignalR Конечные точки
Когда IApiEndpointMetadata присутствует, cookie обработчик проверки подлинности возвращает соответствующие коды состояния HTTP (401 для неаутентифицированных запросов, 403 для запрещенных запросов) вместо перенаправления.
Если вы хотите предотвратить это новое поведение и всегда перенаправлять на URI входа и отказа в доступе для неаутентифицированных или ненавторизованных запросов независимо от целевой конечной точки, вы можете переопределить RedirectToLogin и RedirectToAccessDenied события следующим образом:
builder.Services.AddAuthentication()
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
});
Дополнительные сведения об этом критическом изменении см. в разделе ASP.NET Core объявление о критических изменениях.
Miscellaneous
В этом разделе описаны другие функции в .NET 10.
Настройка диагностики обработчика исключений с подавлением
В промежуточный слой обработчика исключений в ASP.NET Core добавлен новый параметр конфигурации для управления выходными данными диагностики: ExceptionHandlerOptions.SuppressDiagnosticsCallback. Этот обратный вызов передает контекст о запросе и исключении, что позволяет добавить логику, которая определяет, следует ли этот промежуточный компонент записывать журналы ошибок и другую телеметрию.
Этот параметр полезен, если вы знаете, что исключение является временным или обрабатывается ПО промежуточного слоя обработчика исключений, и вы не хотите записывать журналы ошибок на платформу наблюдаемости.
Поведение по умолчанию ПО промежуточного слоя также изменилось: он больше не записывает диагностику исключений для исключений, обрабатываемых IExceptionHandler. На основе отзывов пользователей ведение журнала об обрабатываемых исключениях на уровне ошибки часто является нежелательным, когда IExceptionHandler.TryHandleAsync возвращает true.
Вы можете вернуться к предыдущему поведению, настроив SuppressDiagnosticsCallback:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false;
});
Дополнительные сведения об этом критическом изменении см. в разделе https://github.com/aspnet/Announcements/issues/524.
Поддержка домена Top-Level .localhost
Домен .localhost верхнего уровня (TLD) определяется в RFC2606 и RFC6761 как зарезервированный для целей тестирования и доступен для пользователей локально, как любое другое доменное имя. Это означает, что использование такого имени, как myapp.localhost локально, разрешающее ip-адрес обратного цикла, разрешено и ожидается в соответствии с этими RFCs. Кроме того, современные браузеры evergreen уже автоматически разрешают любое имя *.localhost на IP-адрес обратной связи (127.0.0.1/::1), что эффективно превращает их в псевдоним для любой службы, уже размещенной по адресу localhost на локальном компьютере. Это означает, что любая служба, отвечающая на http://localhost:6789, также будет отвечать на http://anything-here.localhost:6789, если дополнительная проверка имени узла или ее принудительное применение не выполняется службой.
ASP.NET Core был обновлён в .NET 10 версия 7 предварительного выпуска для лучшей поддержки .localhost TLD, чтобы его можно было легко использовать при создании и запуске приложений ASP.NET Core в локальной среде разработки. Наличие разных приложений, работающих локально, можно разрешить с помощью разных имен, что позволяет лучше разделить некоторые ресурсы веб-сайта, связанные с доменным именем, например файлы cookie, и упростить определение приложения, которое вы просматриваете с помощью имени, отображаемого в адресной строке браузера.
встроенный HTTP-сервер ASP.NET Core Kestrel теперь корректно обрабатывает любое имя *.localhost, определенное через поддерживаемые механизмы конфигурации конечной точки, как локальный адрес и привязывается к нему, а не ко всем внешним адресам (т.е., к 127.0.0.1/::1 вместо 0.0.0.0/::). Это включает "applicationUrl" свойство в профилях запуска, настроенных в файле launchSettings.json, и в переменной среды ASPNETCORE_URLS. При настройке прослушивания .localhost адреса Kestrel записывается информационное сообщение для обоих .localhostадресовlocalhost , чтобы было ясно, что оба имени можно использовать.
Хотя веб-браузеры автоматически разрешают *.localhost имена в локальный адрес обратного цикла, другие приложения могут рассматривать *.localhost имена как обычные доменные имена и пытаться разрешить их с помощью соответствующего стека DNS. Если конфигурация DNS не разрешает *.localhost имена в адрес, не удается подключиться. Вы можете продолжать использовать обычное localhost имя для обращения к вашим приложениям, если вы не используете веб-браузер.
Сертификат разработки HTTPS ASP.NET Core (включая команду dotnet dev-certs https) обновлен, чтобы убедиться, что сертификат действителен для использования с доменным именем *.dev.localhost. После установки .NET 10 sdk preview 7 доверяйте новому сертификату разработчика, выполнив dotnet dev-certs https --trust в командной строке, чтобы убедиться, что система настроена для доверия новому сертификату.
Сертификат перечисляет *.dev.localhost имя в качестве альтернативного имени субъекта (SAN), а не *.localhost, поскольку использование подстановочного сертификата для доменного имени верхнего уровня недопустимо.
Шаблоны проектов для ASP.NET Core Empty (web) и Blazor Web App (blazor) были обновлены новым параметром, который при указании настраивает созданный проект для использования суффикса имени домена .dev.localhost, объединяя его с именем проекта, чтобы приложение можно было просматривать по адресу, например https://myapp.dev.localhost:5036:
$ dotnet new web -n MyApp --localhost-tld
The template "ASP.NET Core Empty" was created successfully.
Processing post-creation actions...
Restoring D:\src\MyApp\MyApp.csproj:
Restore succeeded.
$ cd .\MyApp\
$ dotnet run --launch-profile https
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://myapp.dev.localhost:7099
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7099/
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://myapp.dev.localhost:5036
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5036/
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: D:\src\local\10.0.1xx\MyApp
Поддержка десериализации Json+PipeReader в MVC и минимальных API
PR: https://github.com/dotnet/aspnetcore/pull/62895
MVC, минимальные API и методы HttpRequestJsonExtensions.ReadFromJsonAsync были обновлены, чтобы использовать новую поддержку Json+PipeReader без необходимости в изменении кода приложений.
Для большинства приложений добавление этой поддержки не влияет на их поведение. Тем не менее, если приложение использует пользовательскую JsonConverter, есть вероятность того, что преобразователь не обрабатывает Utf8JsonReader.HasValueSequence правильно. Это может привести к отсутствию данных и ошибок, таких как ArgumentOutOfRangeException, при десериализации.
Краткое решение (особенно если используемый вами пользовательский JsonConverter находится не в вашей собственности) — переключить "Microsoft.AspNetCore.UseStreamBasedJsonParsing"AppContext на "true". Это должно быть временное решение, и JsonConverter необходимо обновить для того, чтобы поддерживать HasValueSequence.
Чтобы исправить JsonConverter реализации, можно воспользоваться быстрым решением, которое выделяет массив из ReadOnlySequence и будет выглядеть следующим образом:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
Существует также более сложное (но производительное) исправление, которое будет включать отдельный путь кода для ReadOnlySequence обработки:
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
Автоматическое удаление из пула памяти
Пулы памяти, используемые службами KestrelIIS и HTTP.sys теперь автоматически вытесняют блоки памяти, когда приложение неактивно или находится под меньшей нагрузкой. Функция выполняется автоматически и не должна быть включена или настроена вручную.
Почему важно освобождение памяти
Ранее память, выделенная пулом, остается зарезервированной, даже если она не используется. Эта функция освобождает память обратно в систему, когда приложение неактивно в течение определенного периода времени. Это вытеснение снижает общее использование памяти и помогает приложениям реагировать на различные рабочие нагрузки.
Использование метрик вытеснения памяти
Метрики были добавлены в пул памяти по умолчанию, используемый нашими реализациями сервера. Новые метрики находятся под именем "Microsoft.AspNetCore.MemoryPool".
Для получения информации о метриках и их использовании, см. метрики ASP.NET Core.
Управление пулами памяти
Помимо использования пулов памяти более эффективно путем вытеснения ненужных блоков памяти, .NET 10 улучшает возможности создания пулов памяти. Это делается путем предоставления встроенной IMemoryPoolFactory и реализации MemoryPoolFactory. Она обеспечивает доступность реализации для приложения с помощью внедрения зависимостей.
В следующем примере кода показана простая фоновая служба, которая использует встроенную реализацию фабрики пулов памяти для создания пулов памяти. Эти пулы пользуются функцией автоматического вытеснения:
public class MyBackgroundService : BackgroundService
{
private readonly MemoryPool<byte> _memoryPool;
public MyBackgroundService(IMemoryPoolFactory<byte> factory)
{
_memoryPool = factory.Create();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(20, stoppingToken);
// do work that needs memory
var rented = _memoryPool.Rent(100);
rented.Dispose();
}
catch (OperationCanceledException)
{
return;
}
}
}
}
Чтобы использовать собственную фабрику пула памяти, создайте класс, который реализует IMemoryPoolFactory, и зарегистрируйте его с помощью внедрения зависимостей, как показано в следующем примере. Пулы памяти, созданные таким образом, не пользуются преимуществом функции автоматического вытеснения, если вы не реализуете аналогичную логику вытеснения в вашей пользовательской фабрике.
services.AddSingleton<IMemoryPoolFactory<byte>,
CustomMemoryPoolFactory>();
public class CustomMemoryPoolFactory : IMemoryPoolFactory<byte>
{
public MemoryPool<byte> Create()
{
// Return a custom MemoryPool implementation
// or the default, as is shown here.
return MemoryPool<byte>.Shared;
}
}
Настраиваемые дескрипторы безопасности для HTTP.sys
Теперь можно указать пользовательский дескриптор безопасности для очередей запросов HTTP.sys. Новое свойство RequestQueueSecurityDescriptor в HttpSysOptions обеспечивает более детальный контроль прав доступа для очереди запросов. Этот детализированный элемент управления позволяет настроить безопасность в соответствии с потребностями приложения.
Что можно сделать с новым свойством
Очередь запросов в HTTP.sys — это структура уровня ядра, которая временно сохраняет входящие HTTP-запросы, пока приложение не будет готово к обработке. Настраивая дескриптор безопасности, вы можете разрешить или запретить определенным пользователям или группам доступ к очереди запросов. Это полезно в сценариях, где требуется ограничить или делегировать обработку запросов HTTP.sys на уровне операционной системы.
Использование нового свойства
Свойство RequestQueueSecurityDescriptor применяется только при создании новой очереди запросов. Это свойство не влияет на существующие очереди запросов. Чтобы использовать это свойство, задайте его экземпляром GenericSecurityDescriptor при настройке сервера HTTP.sys.
Например, следующий код разрешает всем прошедшим проверку подлинности пользователям, но запрещает гостям:
using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.AspNetCore.Server.HttpSys;
// Create a new security descriptor
var securityDescriptor = new CommonSecurityDescriptor(isContainer: false, isDS: false, sddlForm: string.Empty);
// Create a discretionary access control list (DACL)
var dacl = new DiscretionaryAcl(isContainer: false, isDS: false, capacity: 2);
dacl.AddAccess(
AccessControlType.Allow,
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
-1,
InheritanceFlags.None,
PropagationFlags.None
);
dacl.AddAccess(
AccessControlType.Deny,
new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null),
-1,
InheritanceFlags.None,
PropagationFlags.None
);
// Assign the DACL to the security descriptor
securityDescriptor.DiscretionaryAcl = dacl;
// Configure HTTP.sys options
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseHttpSys(options =>
{
options.RequestQueueSecurityDescriptor = securityDescriptor;
});
Дополнительные сведения см. в реализации веб-сервера HTTP.sys в ASP.NET Core.
Улучшена поддержка тестирования приложений с помощью инструкций верхнего уровня
.NET 10 теперь лучше поддерживает тестирование приложений, использующих инструкции top-level. Ранее разработчикам пришлось вручную добавить public partial class Program в файл Program.cs, чтобы тестовый проект мог ссылаться на Program class.
public partial class Program требуется, так как функция оператора верхнего уровня в C# 9 создала объект Program class , объявленный как внутренний.
В .NET 10 генератор исходного кода source generator используется для создания объявления public partial class Program, если программист не объявил его явным образом. Кроме того, был добавлен анализатор, чтобы определить, когда public partial class Program объявлен явным образом, и рекомендовать разработчику его удалить.
Следующие PR-ы, внесшие вклад в эту функцию:
Новая реализация патча JSON с помощью System.Text.Json
- Стандартный формат описания изменений, применяемых к документу JSON.
- Определяется в RFC 6902 и широко используется в API RESTful для выполнения частичных обновлений ресурсов JSON.
- Представляет последовательность операций (например, Add, Remove, Replace, Move, Copy, Test), которые можно применить для изменения документа JSON.
В веб-приложениях JSON Patch обычно используется в операции PATCH для частичного обновления ресурса. Вместо отправки всего ресурса для обновления клиенты могут отправлять документ исправления JSON, содержащий только изменения. Установка исправлений уменьшает размер полезной нагрузки и повышает эффективность.
В этом выпуске представлена новая реализация Microsoft.AspNetCore.JsonPatch, основанная на сериализации System.Text.Json. Эта функция:
- Соответствует современным методикам .NET путем использования библиотеки
System.Text.Json, оптимизированной для .NET. - Обеспечивает улучшенную производительность и сокращение использования памяти по сравнению с устаревшей реализацией
Newtonsoft.Json.
Следующие тесты сравнивают производительность новой System.Text.Json реализации с устаревшей реализацией Newtonsoft.Json .
| Scenario | Implementation | Mean | Выделенная память |
|---|---|---|---|
| Тесты приложений | Newtonsoft.JsonPatch | 271,924 мs | 25 КБ |
| System.Text.JsonPatch | 1,584 мкс | 3 КБ | |
| Тесты десериализации | Newtonsoft.JsonPatch | 19,261 мкс | 43 КБ |
| System.Text.JsonPatch | 7,917 мкс | 7 КБ |
Эти тесты подчеркивают значительный рост производительности и сокращение использования памяти с новой реализацией.
Notes:
- Новая реализация не является заменой устаревшей реализации. В частности, новая реализация не поддерживает динамические типы, например ExpandoObject.
- Стандарт JSON Patch имеет присущие риски безопасности. Так как эти риски присущи стандарту исправлений JSON, новая реализация не пытается устранить связанные с ней риски безопасности. Разработчик отвечает за безопасное применение документа исправления JSON к целевому объекту. Дополнительные сведения см. в разделе "Устранение рисков безопасности ".
Usage
Чтобы включить поддержку исправлений JSON с помощью System.Text.Json, установите пакет NuGet Microsoft.AspNetCore.JsonPatch.SystemTextJson.
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
Этот пакет предоставляет JsonPatchDocument<T> класс для представления документа JSON Patch для объектов типа T и пользовательскую логику для сериализации и десериализации JSON Patch документов с помощью System.Text.Json. Ключевой метод класса JsonPatchDocument<T> — это ApplyTo, который применяет операции патчей к целевому объекту типа T.
В следующих примерах показано, как использовать ApplyTo метод для применения документа JSON Patch к объекту.
Пример. Применение JsonPatchDocument
В следующем примере показано:
- Операции
add,replaceиremove. - Операции с вложенными свойствами.
- Добавление нового элемента в массив.
- Использование преобразователя строк JSON-перечисления в документе исправления JSON.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com",
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
Address = new Address
{
Street = "123 Main St",
City = "Anytown",
State = "TX"
}
};
// Raw JSON Patch document
var jsonPatch = """
[
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
{ "op": "remove", "path": "/Email"},
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
{
"op": "add",
"path": "/PhoneNumbers/-",
"value": { "Number": "987-654-3210", "Type": "Work" }
}
]
""";
// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON Patch document
patchDoc!.ApplyTo(person);
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
// Output:
// {
// "firstName": "Jane",
// "lastName": "Doe",
// "address": {
// "street": "123 Main St",
// "city": "Anytown",
// "state": "TX",
// "zipCode": "90210"
// },
// "phoneNumbers": [
// {
// "number": "123-456-7890",
// "type": "Mobile"
// },
// {
// "number": "987-654-3210",
// "type": "Work"
// }
// ]
// }
Метод ApplyTo обычно следует соглашениям и параметрам System.Text.Json обработки JsonPatchDocument, включая поведение, управляемое следующими параметрами:
-
NumberHandling: считываются ли числовые свойства из строк. -
PropertyNameCaseInsensitive: Являются ли имена свойств чувствительными к регистру.
Основные различия между System.Text.Json и новой JsonPatchDocument<T> реализацией:
- Тип среды выполнения целевого объекта, а не объявленного типа, определяет, какие свойства
ApplyToпатчи. -
System.Text.Jsonдесериализация зависит от объявленного типа для определения подходящих свойств.
Пример: применение JsonPatchDocument с обработкой ошибок
При применении документа исправления JSON могут возникать различные ошибки. Например, целевой объект может не иметь указанного свойства, или указанное значение может быть несовместимо с типом свойства.
JSON Patch также поддерживает операцию test. Операция test проверяет, равно ли указанное значение целевому свойству, а если нет, возвращает ошибку.
В следующем примере показано, как корректно обрабатывать эти ошибки.
Important
Объект, переданный методу ApplyTo , изменяется на месте. Это ответственность вызывающего абонента отменить эти изменения, если любая операция завершается ошибкой.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com"
};
// Raw JSON Patch document
var jsonPatch = """
[
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
{ "op": "test", "path": "/FirstName", "value": "Jane" },
{ "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";
// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON Patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
{
errors ??= new ();
var key = jsonPatchError.AffectedObject.GetType().Name;
if (!errors.ContainsKey(key))
{
errors.Add(key, new string[] { });
}
errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
});
if (errors != null)
{
// Print the errors
foreach (var error in errors)
{
Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
}
}
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
// Output:
// Error in Person: The current value 'John' at path 'FirstName' is not equal
// to the test value 'Jane'.
// {
// "firstName": "John",
// "lastName": "Smith", <<< Modified!
// "email": "janedoe@gmail.com", <<< Modified!
// "phoneNumbers": []
// }
Снижение рисков безопасности
При использовании пакета Microsoft.AspNetCore.JsonPatch.SystemTextJson важно понимать и устранять потенциальные риски безопасности. В следующих разделах описаны выявленные риски безопасности, связанные с исправлением JSON, и рекомендуемые способы устранения рисков для обеспечения безопасного использования пакета.
Important
Это не исчерпывающий список угроз. Разработчики приложений должны проводить собственные проверки модели угроз, чтобы определить полный список конкретных приложений и при необходимости придумать соответствующие меры по устранению рисков. Например, приложения, которые делают коллекции доступными для операций исправления, должны учитывать потенциал для атак алгоритмической сложности, если эти операции вставляют или удаляют элементы в начале коллекции.
Выполняя комплексные модели угроз для собственных приложений и устраняя выявленные угрозы, следуя приведенным ниже рекомендациям, потребители этих пакетов могут интегрировать функции исправления JSON в свои приложения, минимизируя риски безопасности.
Потребители этих пакетов могут интегрировать функции исправлений JSON в свои приложения, минимизируя риски безопасности, в том числе:
- Проведение комплексных моделей угроз для их собственных приложений.
- Устранение выявленных угроз.
- Следуйте рекомендациям по устранению рисков в следующих разделах.
Отказ в обслуживании (DoS) через увеличение памяти
-
Сценарий: вредоносный клиент отправляет
copyоперацию, которая дублирует графы больших объектов несколько раз, что приводит к чрезмерному потреблению памяти. - Влияние: потенциальные условия исчерпанияOf-Memory (OOM), приводящие к сбоям в работе службы.
-
Mitigation:
- Перед вызовом
ApplyToпроверьте входящие JSON-патчи на соответствие размера и структуры. - Проверка должна быть специфичной для приложения, но пример проверки может выглядеть следующим образом:
- Перед вызовом
public void Validate(JsonPatchDocument<T> patch)
{
// This is just an example. It's up to the developer to make sure that
// this case is handled properly, based on the app's requirements.
if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
> MaxCopyOperationsCount)
{
throw new InvalidOperationException();
}
}
Подверсия бизнес-логики
- Сценарий. Операции исправления могут управлять полями с неявными инвариантами (например, внутренними флагами, идентификаторами или вычисляемыми полями), нарушая бизнес-ограничения.
- Влияние: проблемы целостности данных и непреднамеренное поведение приложения.
-
Mitigation:
- Используйте объекты POCO с явно определенными свойствами, безопасными для изменения.
- Избегайте предоставления конфиденциальных или критически важных свойств безопасности в целевом объекте.
- Если объект POCO не используется, проверьте исправленный объект после применения операций, чтобы убедиться, что бизнес-правила и инварианты не нарушаются.
Проверка подлинности и авторизация
- Сценарий. Неавторизованные или несанкционированные клиенты отправляют вредоносные запросы на исправление JSON.
- Влияние: несанкционированный доступ к изменению конфиденциальных данных или нарушению поведения приложения.
-
Mitigation:
- Защита конечных точек, принимаюющих запросы на исправление JSON с помощью надлежащих механизмов проверки подлинности и авторизации.
- Ограничить доступ к доверенным клиентам или пользователям с соответствующими разрешениями.
Определение локального URL-адреса с помощью RedirectHttpResult.IsLocalUrl
Используйте новый вспомогательный метод RedirectHttpResult.IsLocalUrl(url), чтобы определить, является ли URL-адрес локальным. URL-адрес считается локальным, если выполняются следующие условия:
- В нём отсутствуют раздел хост или авторизация.
- Он имеет абсолютный путь.
URL-адреса с виртуальными путями "~/" также являются локальными.
IsLocalUrl Полезно для проверки URL-адресов перед перенаправлением на них, чтобы предотвратить открытые атаки перенаправления.
if (RedirectHttpResult.IsLocalUrl(url))
{
return Results.LocalRedirect(url);
}
Спасибо @martincostello за этот вклад!
Кардинальные изменения
Используйте статьи в Критические изменения в .NET для поиска изменений, которые могут применяться при обновлении приложения до более новой версии .NET.
ASP.NET Core