Новые возможности ASP.NET Core 9.0
В этой статье рассматриваются наиболее значительные изменения в ASP.NET Core 9.0 со ссылками на соответствующую документацию.
Оптимизация доставки статических ресурсов
MapStaticAssets
Соглашения о конечной точке маршрутизации — это новая функция, которая оптимизирует доставку статических ресурсов в приложениях ASP.NET Core.
Сведения о доставке статических ресурсов для Blazor приложений см. в разделе ASP.NET Core Blazor статические файлы.
Для следования передовым стандартам в обслуживании статических ресурсов требуется значительный объем работы и технических знаний. Без оптимизаций, таких как сжатие, кэширование и отпечатки пальцев:
- Браузер должен выполнять дополнительные запросы на каждую загрузку страницы.
- Более байтов, чем необходимо, передаются через сеть.
- Иногда устаревшие версии файлов предоставляются клиентам.
Для создания выполняющихся веб-приложений требуется оптимизация доставки ресурсов в браузер. Возможные оптимизации:
- Обслуживают указанный ресурс один раз, пока файл не изменится или браузер очищает его кэш. Задайте заголовок ETag.
- Запретить браузеру использовать старые или устаревшие ресурсы после обновления приложения. Задайте заголовок Last-Modified.
- Настройте правильные заголовки кэширования.
- Используйте промежуточное программное обеспечение для кэширования.
- По возможности обслуживайте сжатые версии ресурсов.
- Используйте CDN для обслуживания ресурсов ближе к пользователю.
- Свести к минимуму размер ресурсов, обслуживаемых браузером. Эта оптимизация не включает минификации.
MapStaticAssets — это новая функция, которая оптимизирует доставку статических ресурсов в приложении. Она предназначена для работы со всеми платформами пользовательского интерфейса, включая Blazor, Razor Страницы и MVC. Обычно это замена UseStaticFiles
для:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
+app.MapStaticAssets();
-app.UseStaticFiles();
app.MapRazorPages();
app.Run();
MapStaticAssets
работает путем объединения процессов сборки и публикации для сбора сведений обо всех статических ресурсах в приложении. Затем эти сведения используются библиотекой среды выполнения для эффективного обслуживания этих файлов в браузере.
MapStaticAssets
может заменить UseStaticFiles
в большинстве случаев; однако он оптимизирован для обслуживания ресурсов, о которых приложение знает на этапе сборки и публикации. Если приложение обслуживает ресурсы из других расположений, таких как диск или внедренные ресурсы, UseStaticFiles
следует использовать.
MapStaticAssets
предоставляет следующие преимущества, которых нет у UseStaticFiles
:
- Сжатие времени сборки для всех ресурсов в приложении:
-
gzip
во время разработки иgzip + brotli
во время публикации. - Все ресурсы сжимаются с целью уменьшения размера ресурсов до минимального.
-
- На основе
ETags
содержимого:Etags
для каждого ресурса представляют собой строку, закодированную в формате Base64 с хэшем SHA-256 содержимого. Это гарантирует, что браузер перезагрузит файл только в том случае, если его содержимое изменилось.
В следующей таблице показаны исходные и сжатые размеры CSS и JS файлов в шаблоне Pages по умолчанию Razor :
Файлы | Исходная | Сжатый | Сокращение % |
---|---|---|---|
bootstrap.min.css | 163 | 17,5 | 89.26% |
jquery.js | 89.6 | 28 | 68.75% |
bootstrap.min.js | 78,5 | 20 | 74.52% |
Всего | 331.1 | 65,5 | 80.20% |
В следующей таблице показаны исходные и сжатые размеры при использовании библиотеки компонентов Fluent UI:
Файлы | Исходная | Сжатый | Сокращение % |
---|---|---|---|
fluent.js | 384 | 73 | 80.99% |
fluent.css | 94 | 11 | 88.30% |
Всего | 478 | 84 | 82.43% |
В несжатом виде 478 КБ были уменьшены до 84 КБ в сжатом виде.
В следующей таблице показаны исходные и сжатые размеры с помощью библиотеки компонентов MudBlazorBlazor :
Файлы | Оригинал | Сжатый | Сокращение |
---|---|---|---|
MudBlazor.min.css | 541 | 37,5 | 93.07% |
MudBlazor.min.js | 47,4 | 9,2 | 80.59% |
Всего | 588,4 | 46,7 | 92.07% |
Оптимизация происходит автоматически при использовании MapStaticAssets
. При добавлении или обновлении библиотеки, например с новым JavaScript или CSS, ресурсы оптимизируются в процессе сборки. Оптимизация особенно полезна для мобильных сред, которые могут иметь более низкую пропускную способность или ненадежные подключения.
Дополнительные сведения о новых функциях доставки файлов см. в следующих ресурсах:
Включение динамического сжатия на сервере и использование MapStaticAssets
MapStaticAssets
имеет следующие преимущества по сравнению с динамическим сжатием на сервере:
- Проще, так как нет конкретной конфигурации сервера.
- Более эффективна, так как ресурсы сжимаются во время сборки.
- Позволяет разработчику тратить дополнительное время во время процесса сборки, чтобы обеспечить минимальный размер ресурсов.
Рассмотрим следующую таблицу сравнения сжатия MudBlazor с динамическим сжатием IIS и MapStaticAssets
:
сжатие gzip в IIS | MapStaticAssets |
MapStaticAssets уменьшение |
---|---|---|
≅ 90 | 37,5 | 59 % |
Blazor
В этом разделе описываются новые возможности для Blazor.
.NET MAUI Blazor Hybrid и шаблон решения для веб-приложения
Новый шаблон решения упрощает создание .NET MAUI собственных и Blazor веб-клиентских приложений, использующих один и тот же пользовательский интерфейс. В этом шаблоне показано, как создавать клиентские приложения с максимальным использованием кода, нацеленные на платформы Android, iOS, Mac, Windows и Веб.
К ключевым функциям этого шаблона относятся:
- Возможность выбора интерактивного Blazor режима отрисовки для веб-приложения.
- Автоматическое создание соответствующих проектов, включая Blazor Web App (глобальную интерактивную отрисовку) и .NET MAUIBlazor Hybrid приложение.
- Созданные проекты используют общую Razor библиотеку классов (RCL) для обслуживания компонентов пользовательского Razor интерфейса.
- Пример кода включен, демонстрирующий использование внедрения зависимостей для предоставления различных реализаций интерфейса для приложения Blazor Hybrid и Blazor Web App.
Чтобы приступить к работе, установите пакет SDK для .NET 9 и установите рабочую .NET MAUI нагрузку, содержащую шаблон:
dotnet workload install maui
Создайте решение из шаблона проекта в командной оболочке с помощью следующей команды:
dotnet new maui-blazor-web
Шаблон также доступен в Visual Studio.
Примечание.
В настоящее время возникает исключение, если Blazor режимы отрисовки определяются на уровне каждой страницы или каждого компонента. Для получения дополнительной информации см. BlazorWebView требует способа для разрешения переопределения ResolveComponentForRenderMode (dotnet/aspnetcore
#51235).
Для получения дополнительной информации см. статью Создание приложения .NET MAUIBlazor Hybrid с помощью Blazor Web App.
Обнаружение расположения отрисовки, интерактивности и назначенного режима отрисовки во время выполнения
Мы представили новый API, предназначенный для упрощения процесса запроса состояний компонентов во время выполнения. Этот API предоставляет следующие возможности:
- Определите текущее расположение выполнения компонента: это может быть полезно для отладки и оптимизации производительности компонентов.
- Проверьте, работает ли компонент в интерактивной среде: это может быть полезно для компонентов, которые имеют разные поведения в зависимости от интерактивности среды.
- Получите назначенный режим отрисовки для компонента: понимание режима отрисовки может помочь в оптимизации процесса отрисовки и повышении общей производительности компонента.
Дополнительные сведения см. в
Улучшена возможность повторного подключения на стороне сервера:
Были внесены следующие улучшения в опыт повторного подключения на стороне сервера по умолчанию:
Когда пользователь возвращается к приложению с отключенным каналом, попытка повторного подключения выполняется немедленно, а не после ожидания следующего интервала повторного подключения. Это улучшает взаимодействие с пользователем при переходе к приложению на вкладке браузера, которая ушла в спящий режим.
Когда попытка повторного подключения достигает сервера, но сервер уже разорвал соединение, происходит автоматическая перезагрузка страницы. Это предотвращает необходимость вручную обновлять страницу пользователем, если это, вероятно, приведет к успешному повторному подключению.
Время повторного подключения использует вычисленную стратегию обратного выхода. По умолчанию первые несколько попыток повторного подключения происходят в быстрой последовательности без интервала повторной попытки, прежде чем между попытками вводятся вычисленные задержки. Поведение интервала повтора можно настроить, указав функцию для вычисления интервала повтора, как показано в следующем примере экспоненциального уменьшения:
Blazor.start({ circuit: { reconnectionOptions: { retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000 }, }, });
Стилизация пользовательского интерфейса повторного подключения по умолчанию была обновлена.
Дополнительные сведения см. в статье Руководство по ASP.NET Core BlazorSignalR.
Упрощенная сериализация состояния аутентификации для Blazor Web Apps
Новые API упрощают добавление проверки подлинности в существующий Blazor Web App. При создании нового Blazor Web App с проверкой подлинности с помощью Individual Accounts и включенной интерактивностью на основе WebAssembly, проект включает настраиваемый AuthenticationStateProvider, как в серверном, так и в клиентском проекте.
Эти поставщики передают статус аутентификации пользователя в браузер. Проверка подлинности на сервере, а не клиент позволяет приложению получать доступ к состоянию проверки подлинности во время предварительной подготовки и до инициализации среды выполнения .NET WebAssembly.
Пользовательские AuthenticationStateProvider реализации используют службу состояния сохраняемого компонента (PersistentComponentState) для сериализации состояния проверки подлинности в примечаниях HTML и считывания его обратно из WebAssembly для создания нового AuthenticationState экземпляра.
Это работает хорошо, если вы начали работу с Blazor Web App шаблона проекта и выбрали параметр Отдельные учетные записи, но вам придется реализовать или скопировать значительный объем кода, если вы пытаетесь добавить аутентификацию в существующий проект. Теперь существуют API- интерфейсы, которые теперь являются частью Blazor Web App шаблона проекта, которые можно вызывать в проектах сервера и клиента, чтобы добавить эту функцию:
- AddAuthenticationStateSerialization: добавляет необходимые службы для сериализации состояния проверки подлинности на сервере.
- AddAuthenticationStateDeserialization: добавляет необходимые службы для десериализации состояния аутентификации в браузере.
По умолчанию API сериализует только серверное имя и утверждения роли для доступа в браузере. Параметр можно передать в AddAuthenticationStateSerialization для включения всех требований.
Для получения дополнительной информации см. следующие разделы ASP.NET Core о аутентификации и авторизации:
- Blazor Identity Пользовательский интерфейс (отдельные учетные записи)
- Управление состоянием проверки подлинности в Blazor Web Apps
Добавление страниц отрисовки на стороне статического сервера (SSR) в глобальный интерактивный Blazor Web App
Выпуск .NET 9 упростил добавление статических SSR-страниц в приложения с глобальной интерактивностью.
Этот подход полезен только в том случае, если приложение имеет определённые страницы, которые не могут работать с интерактивной отрисовкой на сервере или с помощью WebAssembly. Например, используйте этот подход для страниц, которые зависят от чтения и записи файлов cookie HTTP и могут работать только в цикле запроса или ответа вместо интерактивной отрисовки. Для страниц, работающих с интерактивной отрисовкой, их не следует заставлять использовать статическую отрисовку SSR, так как это менее эффективно и менее гибко для конечного пользователя.
Пометьте любую Razor страницу компонента с новым[ExcludeFromInteractiveRouting]
атрибутом, назначенным директивой@attribute
Razor:
@attribute [ExcludeFromInteractiveRouting]
Применение атрибута приводит к переходу на страницу для выхода из интерактивной маршрутизации. Входящая навигация принудительно выполняет полную перезагрузку страницы вместо обработки страницы через интерактивную маршрутизацию. Полностраничная перезагрузка заставляет корневой компонент верхнего уровня, как правило, App
компонент (App.razor
), перерисоваться с сервера, что позволяет приложению переключаться на другой режим отрисовки верхнего уровня.
Метод RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting расширения позволяет компоненту определить, применяется ли [ExcludeFromInteractiveRouting]
атрибут к текущей странице.
В компоненте App
используйте шаблон в следующем примере:
- Страницы, которые не помечены атрибутом
[ExcludeFromInteractiveRouting]
, автоматически переключаются на режим отображенияInteractiveServer
с глобальной интерактивностью. Вы можете заменитьInteractiveServer
InteractiveWebAssembly
InteractiveAuto
или указать другой глобальный режим отрисовки по умолчанию. - Страницы, аннотированные атрибутом
[ExcludeFromInteractiveRouting]
, применяют статический SSR, для которогоPageRenderMode
назначаетсяnull
.
<!DOCTYPE html>
<html>
<head>
...
<HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
<Routes @rendermode="@PageRenderMode" />
...
</body>
</html>
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? PageRenderMode
=> HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}
Альтернативой использованию метода расширения RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting является ручное чтение метаданных конечной точки с помощью HttpContext.GetEndpoint()?.Metadata
.
Эта функция рассматривается в справочной документации в ASP.NET Core Blazor режимах рендеринга.
Внедрение конструктора
Razor компоненты поддерживают внедрение конструктора.
В следующем примере частичный класс (code-behind) внедряет NavigationManager
службу с помощью основного конструктора:
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Дополнительные сведения см. в статье Внедрение зависимостей Blazor ASP.NET Core.
Сжатие Websocket для компонентов интерактивного сервера
По умолчанию компоненты интерактивного сервера обеспечивают сжатие для подключений WebSocket и задают директиру frame-ancestors
Политики безопасности контента (CSP) установленную на 'self'
, которая позволяет внедрять приложение только в <iframe>
источник, из которого приложение обслуживается при включении сжатия или при указании конфигурации контекста WebSocket.
Сжатие может быть отключено с помощью параметра ConfigureWebSocketOptions
null
, что снижает уязвимость приложения к атаке , но может привести к снижению производительности:
.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)
Настройте более строгую frame-ancestors
политику безопасности контента CSP со значением 'none'
(необходимы одинарные кавычки), которая позволяет сжатие WebSocket, но препятствует тому, чтобы браузеры внедряли приложение в любой <iframe>
.
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Дополнительные сведения см. на следующих ресурсах:
- Руководство по ASP.NET Core BlazorSignalR
- Руководство по смягчению угроз для интерактивного серверного рендеринга ASP.NET Core Blazor
Обработка событий композиции клавиатуры в Blazor
Новое KeyboardEventArgs.IsComposing
свойство указывает, является ли событие клавиатуры частью сеанса композиции. Отслеживание состояния композиции событий клавиатуры имеет решающее значение для обработки международных методов ввода символов.
Добавлен параметр OverscanCount
в QuickGrid
.
Компонент QuickGrid
теперь предоставляет свойство OverscanCount
, указывающее количество дополнительных строк, отображаемых до и после видимой области при активированной виртуализации.
Значение по умолчанию OverscanCount
— 3. В следующем примере увеличивается OverscanCount
до 4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
InputNumber
компонент поддерживает type="range"
атрибут
Компонент InputNumber<TValue> теперь поддерживает атрибут type="range"
, который создает элемент ввода диапазона, поддерживающий привязку модели и проверку формы, как правило, отображается в виде ползунка или регулятора, а не текстового поля.
<EditForm Model="Model" OnSubmit="Submit" FormName="EngineForm">
<div>
<label>
Nacelle Count (2-6):
<InputNumber @bind-Value="Model!.NacelleCount" max="6" min="2"
step="1" type="range" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private EngineSpecifications? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() {}
public class EngineSpecifications
{
[Required, Range(minimum: 2, maximum: 6)]
public int NacelleCount { get; set; }
}
}
Новые расширенные события навигации
Активируйте JavaScript коллбэки до или после усовершенствованной навигации, используя новые прослушиватели событий.
blazor.addEventListener("enhancednavigationstart", {CALLBACK})
blazor.addEventListener("enhancednavigationend", {CALLBACK})
Дополнительные сведения см. в разделе ASP.NET Core Blazor JavaScript со статическим отображением на стороне сервера (статический SSR).
Потоковая передача запросов на стороне клиента
Интерактивный рендеринг WebAssembly в Blazor теперь поддерживает потоковую передачу запросов на стороне клиента с помощью параметра request.SetBrowserReqeustStreamingEnabled(true)
в HttpRequestMessage
.
Дополнительные сведения см. на следующих ресурсах:
SignalR
В этом разделе описываются новые особенности для SignalR.
Поддержка полиморфного типа в SignalR Hubs
Теперь методы концентратора могут принимать базовый класс вместо производного класса для включения полиморфных сценариев. Базовый тип должен быть аннотирован, чтобы разрешить полиморфизм.
public class MyHub : Hub
{
public void Method(JsonPerson person)
{
if (person is JsonPersonExtended)
{
}
else if (person is JsonPersonExtended2)
{
}
else
{
}
}
}
[JsonPolymorphic]
[JsonDerivedType(typeof(JsonPersonExtended), nameof(JsonPersonExtended))]
[JsonDerivedType(typeof(JsonPersonExtended2), nameof(JsonPersonExtended2))]
private class JsonPerson
{
public string Name { get; set; }
public Person Child { get; set; }
public Person Parent { get; set; }
}
private class JsonPersonExtended : JsonPerson
{
public int Age { get; set; }
}
private class JsonPersonExtended2 : JsonPerson
{
public string Location { get; set; }
}
Улучшенные действия для SignalR
SignalR теперь имеет ActivitySource для центрального сервера и клиента.
.NET SignalR сервер ActivitySource
SignalR ActivitySource с именем Microsoft.AspNetCore.SignalR.Server
выпускает события для вызовов методов хаба:
- Каждый метод является собственной активностью, поэтому все, что выдает активность во время вызова метода концентратора, находится в рамках активности метода концентратора.
- Действия метода концентратора не имеют родительского элемента. Это означает, что они не объединены в продолжительное SignalR соединение.
В следующем примере используются .NET Aspire панели мониторинга и пакеты OpenTelemetry :
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
Добавьте следующий код запуска в Program.cs
файл:
using OpenTelemetry.Trace;
using SignalRChat.Hubs;
// Set OTEL_EXPORTER_OTLP_ENDPOINT environment variable depending on where your OTEL endpoint is.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
if (builder.Environment.IsDevelopment())
{
// View all traces only in development environment.
tracing.SetSampler(new AlwaysOnSampler());
}
tracing.AddAspNetCoreInstrumentation();
tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
});
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
var app = builder.Build();
Ниже приведен пример выходных данных на панели мониторинга Aspire:
.NET SignalR клиент ActivitySource
SignalR ActivitySource с именем Microsoft.AspNetCore.SignalR.Client
генерирует события для клиента SignalR.
- Клиент .NET SignalR имеет
ActivitySource
с именемMicrosoft.AspNetCore.SignalR.Client
. Теперь вызовы хаба создают клиентский интервал. Обратите внимание, что другие клиенты SignalR, такие как клиент JavaScript, не поддерживают трассировку. Эта функция будет добавлена для большего числа клиентов в будущих версиях. - Вызовы концентратора на клиенте и сервере поддерживают распространение контекста. Распространение контекста трассировки обеспечивает истинную распределенную трассировку. Теперь можно увидеть поток вызовов от клиента к серверу и обратно.
Вот как эти новые действия выглядят в .NET Aspire панели мониторинга:
SignalR поддерживает обрезку и нативную AOT-компиляцию
Продолжая путь Native AOT, начатый в .NET 8, мы включили обрезку и поддержку компиляции AOT для сценариев SignalR клиента и сервера. Теперь вы можете извлечь выгоду из повышения производительности при использовании нативного AOT в приложениях, использующих SignalR для веб-коммуникаций в режиме реального времени.
Начало работы
Установите последний пакет SDK для .NET 9.
Создайте решение из webapiaot
шаблона в командной оболочке с помощью следующей команды:
dotnet new webapiaot -o SignalRChatAOTExample
Замените содержимое Program.cs
файла следующим SignalR кодом:
using Microsoft.AspNetCore.SignalR;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddSignalR();
builder.Services.Configure<JsonHubProtocolOptions>(o =>
{
o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});
var app = builder.Build();
app.MapHub<ChatHub>("/chatHub");
app.MapGet("/", () => Results.Content("""
<!DOCTYPE html>
<html>
<head>
<title>SignalR Chat</title>
</head>
<body>
<input id="userInput" placeholder="Enter your name" />
<input id="messageInput" placeholder="Type a message" />
<button onclick="sendMessage()">Send</button>
<ul id="messages"></ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messages").appendChild(li);
});
async function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
await connection.invoke("SendMessage", user, message);
}
connection.start().catch(err => console.error(err));
</script>
</body>
</html>
""", "text/html"));
app.Run();
[JsonSerializable(typeof(string))]
internal partial class AppJsonSerializerContext : JsonSerializerContext { }
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
В предыдущем примере создается собственный исполняемый файл Windows размером 10 МБ и исполняемый файл Linux размером 10,9 МБ.
Ограничения
- В настоящее время поддерживается только протокол JSON:
- Как показано в предыдущем коде, приложения, использующие сериализацию JSON и собственный AOT, должны использовать
System.Text.Json
генератор источников. - Это соответствует тому же подходу, что и минимальные API.
- Как показано в предыдущем коде, приложения, использующие сериализацию JSON и собственный AOT, должны использовать
- На сервере SignalR параметры метода концентратора типа
IAsyncEnumerable<T>
иChannelReader<T>
, гдеT
является значением типа ValueType (struct
), не поддерживаются. Использование этих типов приводит к исключению среды выполнения при запуске в разработке и в опубликованном приложении. Более подробную информацию см. в статье SignalR: Использование IAsyncEnumerable<T> и ChannelReader<T> с ValueTypes в native AOT (dotnet/aspnetcore
#56179). -
Строго типизированные концентраторы не поддерживаются в Native AOT (
PublishAot
). Использование строго типизированных центров с Native AOT приведет к предупреждениям во время сборки и публикации, а также исключению во время выполнения. Использование строго типизированных хабов с обрезкой (PublishedTrimmed
) поддерживается. - Только
Task
,Task<T>
,ValueTask
илиValueTask<T>
поддерживаются для асинхронных возвращаемых типов.
Минимальные API
В этом разделе описываются новые функции для минимальных API.
В InternalServerError
добавлены аргументы InternalServerError<TValue>
и TypedResults
.
Класс TypedResults является полезным средством для возврата строго типизированных ответов на основе кода состояния HTTP из минимального API.
TypedResults
теперь включает фабричные методы и типы для возврата ответов "500 Внутренняя ошибка сервера" из конечных точек. Ниже приведен пример, который возвращает ответ 500:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Позвоните ProducesProblem
и ProducesValidationProblem
в группах маршрутов
Методы расширения ProducesProblem
и ProducesValidationProblem
были обновлены для поддержки их использования в группах маршрутов. Эти методы указывают на то, что все конечные точки в группе маршрутов могут возвращать ProblemDetails
или ValidationProblemDetails
ответы в целях метаданных OpenAPI.
var app = WebApplication.Create();
var todos = app.MapGroup("/todos")
.ProducesProblem();
todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));
app.Run();
record Todo(int Id, string Title, boolean IsCompleted);
Problem
и ValidationProblem
типы результатов поддерживают построение со значениями IEnumerable<KeyValuePair<string, object?>>
До .NET 9 создание типов результатов Problem и ValidationProblem в минимальных API-интерфейсах требовало, чтобы свойства errors
и extensions
были инициализированы реализацией IDictionary<string, object?>
. В этом выпуске эти API-интерфейсы строительства поддерживают перегрузки, которые используют IEnumerable<KeyValuePair<string, object?>>
.
var app = WebApplication.Create();
app.MapGet("/", () =>
{
var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
return TypedResults.Problem("This is an error with extensions",
extensions: extensions);
});
Спасибо пользователю GitHub joegoldman2 за этот вклад!
OpenAPI
В этом разделе описываются новые возможности OpenAPI
Встроенная поддержка создания документов OpenAPI
Спецификация OpenAPI — это стандарт для описания API-интерфейсов HTTP. Стандарт позволяет разработчикам определять форму API, которые можно подключить к клиентским генераторам, генераторам серверов, средствам тестирования, документации и т. д. В .NET 9 ASP.NET Core обеспечивает встроенную поддержку создания документов OpenAPI, представляющих контроллер или минимальные API через пакет Microsoft.AspNetCore.OpenApi .
Следующие выделенные вызовы кода:
-
AddOpenApi
для регистрации необходимых зависимостей в контейнере DI приложения. -
MapOpenApi
для регистрации необходимых конечных точек OpenAPI в маршрутах приложения.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/hello/{name}", (string name) => $"Hello {name}"!);
app.Run();
Microsoft.AspNetCore.OpenApi
Установите пакет в проекте с помощью следующей команды:
dotnet add package Microsoft.AspNetCore.OpenApi
Запустите приложение и перейдите на openapi/v1.json
, чтобы просмотреть созданный документ OpenAPI.
Документы OpenAPI также можно создать во время сборки, добавив Microsoft.Extensions.ApiDescription.Server
пакет:
dotnet add package Microsoft.Extensions.ApiDescription.Server
Чтобы изменить расположение созданных документов OpenAPI, задайте целевой путь в свойстве OpenApiDocumentsDirectory в файле проекта приложения:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
</PropertyGroup>
Запустите dotnet build
и проверьте созданный JSON-файл в каталоге проекта.
Встроенная в ASP.NET Core генерация документации OpenAPI обеспечивает поддержку различных настроек и параметров. Он предоставляет преобразователи документов, операций и схем и имеет возможность управлять несколькими документами OpenAPI для одного приложения.
Чтобы узнать больше о новых возможностях документов OpenAPI в ASP.NET Core, см. новые документы Microsoft.AspNetCore.OpenApi.
Microsoft.AspNetCore.OpenApi поддерживает тримминг и нативный AOT
OpenAPI в ASP.NET Core поддерживает обрезку и собственный AOT. Следующие шаги по созданию и публикации приложения OpenAPI с обрезкой и собственным AOT:
Создайте проект ASP.NET Core Web API (Native AOT).
dotnet new webapiaot
Добавьте пакет Microsoft.AspNetCore.OpenAPI.
dotnet add package Microsoft.AspNetCore.OpenApi
Обновите Program.cs
, чтобы включить создание документов OpenAPI.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Публикация приложения.
dotnet publish
Проверка подлинности и авторизация
В этом разделе описываются новые функции проверки подлинности и авторизации.
OpenIdConnectHandler добавляет поддержку push-запросов авторизации (PAR)
Мы хотели бы поблагодарить Джо DeCock от Duende Software за добавление push-запросов авторизации (PAR) в ASP.NET Core OpenIdConnectHandler. Джо описал фон и мотивацию включения PAR в своем предложении API следующим образом:
Pushed Authorization Requests (PAR) — это относительно новый стандарт OAuth, который повышает безопасность потоков OAuth и OIDC путем перемещения параметров авторизации из переднего канала в обратный канал. Это значит, что параметры авторизации перемещаются из URL-адресов перенаправления в браузере к прямым http-вызовам между машинами на серверной стороне.
Это предотвращает кибератаку в браузере:
- Просмотр параметров авторизации, которые могут привести к утечке личных данных.
- Изменение этих параметров. Например, кибератака может изменить область запрашиваемого доступа.
При отправке параметров авторизации url-адреса запросов также остаются короткими. Параметры авторизации могут быть очень длинными при использовании более сложных функций OAuth и OIDC, таких как расширенные запросы авторизации. URL-адреса, которые являются длительными, вызывают проблемы во многих браузерах и сетевых инфраструктурах.
Использование PAR поощряется рабочей группой FAPI в Фонде OpenID. Например, для профиля безопасности FAPI2.0 требуется использование PAR. Этот профиль безопасности используется многими группами, работающими на открытых банковских операциях (в первую очередь в Европе), в здравоохранении и в других отраслях с высокими требованиями к безопасности.
PAR поддерживается рядом поставщиков удостоверений, включая
Для .NET 9 мы решили включить PAR по умолчанию, если документ обнаружения поставщика удостоверений содержит информацию о поддержке PAR, так как это должно обеспечить повышенную безопасность для поставщиков, которые поддерживают его. Документ обнаружения поставщика удостоверений обычно находится в .well-known/openid-configuration
. Если это вызывает проблемы, вы можете отключить PAR, настроив OpenIdConnectOptions.PushedAuthorizationBehavior следующим образом:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("oidc", oidcOptions =>
{
// Other provider-specific configuration goes here.
// The default value is PushedAuthorizationBehavior.UseIfAvailable.
// 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
// and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
// of type 'OpenIdConnectOptions' could be found
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
});
Чтобы убедиться, что проверка подлинности выполняется только при использовании PAR, используйте вместо этого PushedAuthorizationBehavior.Require. Это изменение также представляет новое событие OnPushAuthorization для OpenIdConnectEvents , которое можно использовать для настройки запроса принудительной авторизации или его обработки вручную. Более подробную информацию см. в предложении по API.
Настройка OIDC и параметра OAuth
Теперь в обработчиках проверки подлинности OAuth и OIDC есть опция AdditionalAuthorizationParameters
, позволяющая легче настраивать параметры сообщений авторизации, которые обычно включаются в строку запроса перенаправления. В .NET 8 и более ранних версиях это требует настраиваемого метода обратного вызова OnRedirectToIdentityProvider или переопределённого метода BuildChallengeUrl в пользовательском обработчике. Ниже приведен пример кода .NET 8:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("prompt", "login");
context.ProtocolMessage.SetParameter("audience", "https://api.example.com");
return Task.CompletedTask;
};
});
Приведенный выше пример теперь можно упростить в следующем коде:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Настройка флагов расширенной проверки подлинности HTTP.sys
Теперь можно настроить флаги HTTP.sys HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
и HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL
с помощью новых свойств EnableKerberosCredentialCaching
и CaptureCredentials
в AuthenticationManager HTTP.sys для оптимизации обработки аутентификации Windows. Например:
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
options.Authentication.EnableKerberosCredentialCaching = true;
options.Authentication.CaptureCredentials = true;
});
Разное
В следующих разделах описаны другие возможности.
Новая HybridCache
библиотека
Внимание
HybridCache
в настоящее время по-прежнему находится в предварительной версии, но будет полностью выпущен после .NET 9.0 в будущем дополнительном выпуске расширений .NET.
HybridCache
API мостит некоторые пробелы в существующих IDistributedCache и IMemoryCache API. Он также добавляет новые возможности, такие как:
- Защита от лавинных запросов для предотвращения параллельных выборок одной и той же работы.
- Настраиваемая сериализация.
HybridCache
разработан как замена для существующего использования IDistributedCache
и IMemoryCache
и предоставляет простой API для добавления нового кода кэширования. Он предоставляет единый API для кэширования внутри процесса и внепроцессного кэширования.
Чтобы узнать, как HybridCache
api упрощен, сравните его с кодом, который использует IDistributedCache
. Вот пример того, как выглядит использование IDistributedCache
.
public class SomeService(IDistributedCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync
(string name, int id, CancellationToken token = default)
{
var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
var bytes = await cache.GetAsync(key, token); // Try to get from cache.
SomeInformation info;
if (bytes is null)
{
// Cache miss; get the data from the real source.
info = await SomeExpensiveOperationAsync(name, id, token);
// Serialize and cache it.
bytes = SomeSerializer.Serialize(info);
await cache.SetAsync(key, bytes, token);
}
else
{
// Cache hit; deserialize it.
info = SomeSerializer.Deserialize<SomeInformation>(bytes);
}
return info;
}
// This is the work we're trying to cache.
private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
CancellationToken token = default)
{ /* ... */ }
}
Это много работы, чтобы все правильно сделать каждый раз, включая такие аспекты, как сериализация. И в случае промаха кэша вы можете столкнуться с несколькими параллельными потоками, все испытывают промах кэша, все извлекают исходные данные, все их сериализуют и отправляют в кэш.
Сначала, чтобы упростить и улучшить этот код с HybridCache
, необходимо добавить новую библиотеку Microsoft.Extensions.Caching.Hybrid
.
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Зарегистрируйте службу HybridCache
, так же, как вы регистрируете реализацию IDistributedCache
:
builder.Services.AddHybridCache(); // Not shown: optional configuration API.
Теперь большинство проблем кэширования можно выгрузить в HybridCache
:
public class SomeService(HybridCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync
(string name, int id, CancellationToken token = default)
{
return await cache.GetOrCreateAsync(
$"someinfo:{name}:{id}", // Unique key for this combination.
async cancel => await SomeExpensiveOperationAsync(name, id, cancel),
token: token
);
}
}
Мы предоставляем конкретную реализацию абстрактного HybridCache
класса с помощью внедрения зависимостей, но предполагается, что разработчики могут предоставлять пользовательские реализации API. Реализация HybridCache
касается всего, что связано с кэшированием, включая одновременную обработку операций. Маркер cancel
здесь представляет объединенную отмену всех одновременных вызывающих объектов, а не только отмену того вызывающего объекта, которого мы видим (то есть token
).
Сценарии высокой пропускной способности можно оптимизировать с помощью шаблона TState
, чтобы избежать некоторых накладных расходов, связанных с захваченными переменными и обратными вызовами для каждого экземпляра.
public class SomeService(HybridCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
{
return await cache.GetOrCreateAsync(
$"someinfo:{name}:{id}", // unique key for this combination
(name, id), // all of the state we need for the final call, if needed
static async (state, token) =>
await SomeExpensiveOperationAsync(state.name, state.id, token),
token: token
);
}
}
HybridCache
использует настроенную IDistributedCache
реализацию, если она имеется, для вторичного внепроцессного кэширования, например с помощью Redis. Но даже без IDistributedCache
, служба HybridCache
всё равно будет обеспечивать кэширование в процессе и защиту от лавины запросов.
Примечание о повторном использованию объекта
В типичном существующем коде, который использует IDistributedCache
, каждый извлечение объекта из кэша приводит к десериализации. Это означает, что каждый одновременный вызывающий объект получает отдельный экземпляр объекта, который не может взаимодействовать с другими экземплярами. Результатом является безопасность потоков, так как не существует риска параллельных изменений в одном экземпляре объекта.
Поскольку использование HybridCache
будет адаптировано из существующего кода IDistributedCache
, HybridCache
сохраняет это поведение по умолчанию, чтобы избежать возникновения ошибок параллелизма. Однако данный вариант использования изначально является потокобезопасным.
- Если кэшируемые типы неизменяемы.
- Если код не изменяет их.
В таких случаях сообщите HybridCache
, что можно повторно использовать экземпляры, выполняя следующие действия:
- Обозначение типа как
sealed
. Ключевоеsealed
слово в C# означает, что класс не может быть унаследован. - Применение атрибута
[ImmutableObject(true)]
к нему. Атрибут[ImmutableObject(true)]
указывает, что состояние объекта невозможно изменить после его создания.
Повторное использование экземпляров HybridCache
может сократить затраты ресурсов ЦП и объектов, которые связаны с десериализацией каждого вызова. Это может привести к улучшению производительности в сценариях, когда кэшированные объекты являются большими или часто доступны.
Другие HybridCache
функции
Как и IDistributedCache
, HybridCache
поддерживает удаление по ключу с помощью метода RemoveKeyAsync
.
HybridCache
также предоставляет опциональные API для реализаций IDistributedCache
, с целью избежать выделения byte[]
. Эта функция реализуется предварительными версиями пакетов Microsoft.Extensions.Caching.StackExchangeRedis
и Microsoft.Extensions.Caching.SqlServer
.
Сериализация настраивается в рамках регистрации службы с поддержкой обобщенных и типовых сериализаторов с помощью методов WithSerializer
и .WithSerializerFactory
, вызываемых из AddHybridCache
. По умолчанию библиотека управляет string
и byte[]
локально и использует System.Text.Json
для всего остального, но можно использовать protobuf, xml или что-либо другое.
HybridCache
поддерживает более старые среды выполнения .NET, включая платформы .NET Framework 4.7.2 и .NET Standard 2.0.
Дополнительные сведения см. в HybridCache
библиотеке HybridCache в ASP.NET Core
Улучшения страницы исключений разработчика
Страница исключения разработчика ASP.NET Core отображается, когда приложение выдает необработанное исключение во время разработки. Страница исключений разработчика содержит подробные сведения об исключении и запросе.
Третья предварительная версия добавила метаданные конечной точки на страницу исключений для разработчиков. ASP.NET Core использует метаданные конечной точки для управления поведением конечной точки, таких как маршрутизация, кэширование ответов, ограничение скорости, создание OpenAPI и многое другое. На следующем рисунке показаны новые сведения о метаданных в Routing
разделе страницы исключений разработчика:
При тестировании страницы исключений разработчика были определены небольшие улучшения удобства использования. Они поставляются в предварительной версии 4:
- Улучшена оболочка текста. Длинные файлы cookie, значения строк запроса и имена методов больше не добавляют горизонтальные полосы прокрутки браузера.
- Более крупный текст, встречающийся в современных дизайнах.
- Более согласованные размеры таблиц.
На следующем анимированном изображении показана новая страница исключений разработчика:
Улучшения отладки словаря
Отображение словарей и других коллекций "ключ-значение" теперь имеет улучшенную структуру. Ключ отображается в ключевом столбце отладчика вместо объединения со значением. На следующих изображениях показан старый и новый экран словаря в отладчике.
До:
После:
ASP.NET Core имеет множество коллекций key-value. Этот улучшенный интерфейс отладки применяется к следующему:
- Заголовки HTTP
- Строки запросов
- Формы
- Файлы cookie
- Просмотреть данные
- Данные маршрута
- Функции
Исправление для 503 во время перезапуска приложения в IIS
По умолчанию теперь задержка составляет 1 секунду между уведомлением IIS о перезапуске или завершении работы, а также когда ANCM сообщает управляемому серверу начать завершение работы. Задержка настраивается через ANCM_shutdownDelay
переменную среды или задав параметр обработчика shutdownDelay
. Оба значения находятся в миллисекундах. Задержка заключается в основном в уменьшении вероятности гонки, где:
- IIS еще не начали ставить запросы в очередь для нового приложения.
- ANCM начинает отклонять новые запросы, поступающие в старое приложение.
Более медленные компьютеры или компьютеры с высокой нагрузкой на ЦП могут захотеть изменить это значение, чтобы снизить вероятность возникновения ошибки 503.
Пример параметра shutdownDelay
:
<aspNetCore processPath="dotnet" arguments="myapp.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
<handlerSettings>
<!-- Milliseconds to delay shutdown by.
this doesn't mean incoming requests will be delayed by this amount,
but the old app instance will start shutting down after this timeout occurs -->
<handlerSetting name="shutdownDelay" value="5000" />
</handlerSettings>
</aspNetCore>
Исправление находится в глобально установленном модуле ANCM, который поставляется из пакета размещения.
ASP0026: анализатор, предназначенный для предупреждения, когда [Authorize] переопределяется [AllowAnonymous] из дальнего контекста.
Кажется очевидным, что [Authorize]
атрибут, расположенный "ближе" к действию MVC, чем [AllowAnonymous]
, переопределяет [AllowAnonymous]
и требует авторизации. Однако это не обязательно так. Важен относительный порядок атрибутов.
В следующем коде показаны примеры, в которых более близкий [Authorize]
атрибут переопределяется атрибутом [AllowAnonymous]
, который находится дальше.
[AllowAnonymous]
public class MyController
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on the class
public IActionResult Private() => null;
}
[AllowAnonymous]
public class MyControllerAnon : ControllerBase
{
}
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public class MyControllerInherited : MyControllerAnon
{
}
public class MyControllerInherited2 : MyControllerAnon
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public IActionResult Private() => null;
}
[AllowAnonymous]
[Authorize] // Overridden by the preceding [AllowAnonymous]
public class MyControllerMultiple : ControllerBase
{
}
В .NET 9 Preview 6 мы представили анализатор, который будет выделять случаи, когда более близкий атрибут [Authorize]
заменяется на атрибут [AllowAnonymous]
, находящийся дальше от действия MVC. Предупреждение указывает на переопределенный [Authorize]
атрибут со следующим сообщением:
ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away
Правильное действие, выполняемого при отображении этого предупреждения, зависит от намерения атрибутов. Если этот атрибут непреднамеренно подвергает конечную точку анонимным пользователям, следует удалить этот атрибут, который расположен дальше [AllowAnonymous]
.
[AllowAnonymous]
Если атрибут был предназначен для переопределения более близкого [Authorize]
атрибута, вы можете повторить атрибут [AllowAnonymous]
после атрибута [Authorize]
, чтобы прояснить намерение.
[AllowAnonymous]
public class MyController
{
// This produces no warning because the second, "closer" [AllowAnonymous]
// clarifies that [Authorize] is intentionally overridden.
// Specifying AuthenticationSchemes can still be useful
// for endpoints that allow but don't require authenticated users.
[Authorize(AuthenticationSchemes = "Cookies")]
[AllowAnonymous]
public IActionResult Privacy() => null;
}
Улучшенные Kestrel метрики подключения
Мы значительно улучшили Kestrelметрики подключения, включив метаданные о том, почему подключение завершилось сбоем. Теперь kestrel.connection.duration
метрика включает в себя причину закрытия подключения в атрибуте error.type
.
Ниже приведен небольшой пример значений error.type
:
-
tls_handshake_failed
— для подключения требуется TLS, и произошло сбой в процессе рукопожатия TLS. -
connection_reset
— Подключение было неожиданно закрыто клиентом во время выполнения запросов. -
request_headers_timeout
— Kestrel закрыл подключение, так как он не получил заголовки запросов вовремя. -
max_request_body_size_exceeded
— Kestrel закрыло подключение, так как загруженные данные превысили максимальный размер.
Ранее для диагностики Kestrel проблем с подключением требовалось, чтобы сервер записывал подробные, низкоуровневые логи. Однако журналы могут быть дорогостоящими для создания и хранения, и может быть трудно найти нужную информацию среди шума.
Метрики являются гораздо более дешевой альтернативой, которую можно оставить в производственной среде с минимальным воздействием. Собранные метрики могут управлять панелями мониторинга и оповещениями. После определения проблемы на высоком уровне с метриками можно начать дальнейшее исследование с помощью ведения журнала и других инструментов.
Мы ожидаем, что улучшенные метрики подключения будут полезны во многих сценариях:
- Исследование проблем с производительностью, вызванных коротким временем существования подключения.
- Наблюдаются текущие внешние атаки на Kestrel, которые влияют на производительность и стабильность.
- Запись попыток внешних атак на Kestrel, которые встроенная система безопасности Kestrel предотвратила.
Дополнительные сведения см. в разделе ASP.NET Основные метрики.
Настройка Kestrel конечных точек именованного канала
KestrelПоддержка именованных каналов улучшена благодаря расширенным параметрам настройки. Новый CreateNamedPipeServerStream
метод для именованных параметров канала позволяет настраивать каналы для каждой конечной точки.
Пример того, где это полезно, — это приложение, для которого требуются Kestrel две конечные точки канала с разными средствами безопасности доступа. Этот CreateNamedPipeServerStream
параметр можно использовать для создания каналов с настраиваемыми параметрами безопасности в зависимости от имени канала.
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenNamedPipe("pipe1");
options.ListenNamedPipe("pipe2");
});
builder.WebHost.UseNamedPipes(options =>
{
options.CreateNamedPipeServerStream = (context) =>
{
var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName);
return NamedPipeServerStreamAcl.Create(context.NamedPipeEndPoint.PipeName, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity);
};
});
ExceptionHandlerMiddleware
параметр выбора кода состояния на основе типа исключения
Новый параметр при настройке ExceptionHandlerMiddleware
приложения позволяет разработчикам приложений выбрать код состояния, возвращаемый при возникновении исключения во время обработки запросов. Новый параметр изменяет код состояния, установленный в ответе ProblemDetails
из ExceptionHandlerMiddleware
.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Отказ от метрики HTTP в определенных конечных точках и запросах
.NET 9 представляет возможность отказаться от метрики HTTP для определенных конечных точек и запросов. Отключение записи метрик выгодно для конечных узлов, часто вызываемых автоматизированными системами, такими как проверки работоспособности. Регистрация метрик для этих запросов, как правило, не нужна.
HTTP-запросы к конечной точке можно исключить из метрик, добавив метаданные. Любое из следующих:
- Добавьте атрибут
[DisableHttpMetrics]
в контроллер веб-API, концентратор SignalR или службу gRPC. - Вызовите DisableHttpMetrics при инициализации приложения для сопоставления конечных точек.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
Свойство MetricsDisabled
добавлено IHttpMetricsTagsFeature
для:
- Расширенные сценарии, в которых запрос не сопоставляется с конечной точкой.
- Динамическое отключение сбора метрик для определенных HTTP-запросов.
// Middleware that conditionally opts-out HTTP requests.
app.Use(async (context, next) =>
{
var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
if (metricsFeature != null &&
context.Request.Headers.ContainsKey("x-disable-metrics"))
{
metricsFeature.MetricsDisabled = true;
}
await next(context);
});
Поддержка защиты данных для удаления ключей
До версии .NET 9 ключи защиты данных задумано не удалялись, чтобы предотвратить потерю данных. Удаление ключа делает защищенные данные недоступными. Учитывая их небольшой размер, накопление этих ключей обычно представляет минимальное влияние. Однако, чтобы учесть потребности очень длительных служб, мы ввели возможность удаления ключей. Как правило, следует удалить только старые ключи. Удалять ключи можно только в том случае, если вы можете принять риск потери данных в обмен на экономию хранилища. Мы рекомендуем не удалять ключи защиты данных.
using Microsoft.AspNetCore.DataProtection.KeyManagement;
var services = new ServiceCollection();
services.AddDataProtection();
var serviceProvider = services.BuildServiceProvider();
var keyManager = serviceProvider.GetService<IKeyManager>();
if (keyManager is IDeletableKeyManager deletableKeyManager)
{
var utcNow = DateTimeOffset.UtcNow;
var yearAgo = utcNow.AddYears(-1);
if (!deletableKeyManager.DeleteKeys(key => key.ExpirationDate < yearAgo))
{
Console.WriteLine("Failed to delete keys.");
}
else
{
Console.WriteLine("Old keys deleted successfully.");
}
}
else
{
Console.WriteLine("Key manager does not support deletion.");
}
Промежуточное ПО поддерживает Keyed DI
Промежуточное ПО теперь поддерживает Keyed DI как в конструкторе, так и в методе Invoke
/InvokeAsync
.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<MySingletonClass>("test");
builder.Services.AddKeyedScoped<MyScopedClass>("test2");
var app = builder.Build();
app.UseMiddleware<MyMiddleware>();
app.Run();
internal class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next,
[FromKeyedServices("test")] MySingletonClass service)
{
_next = next;
}
public Task Invoke(HttpContext context,
[FromKeyedServices("test2")]
MyScopedClass scopedService) => _next(context);
}
Доверять сертификату разработки ASP.NET Core HTTPS в Linux
В дистрибутивах dotnet dev-certs https --trust
Linux на основе Ubuntu и Fedora теперь настраивается сертификат разработки ASP.NET Core HTTPS в качестве доверенного сертификата:
- Браузеры Chromium, например Google Chrome, Microsoft Edge и Chromium.
- Браузеры на основе Mozilla Firefox и других продуктов Mozilla.
- API .NET, например HttpClient
--trust
Ранее работал только в Windows и macOS. Доверие сертификатов применяется для каждого пользователя.
Чтобы создать доверие к OpenSSL, dev-certs
инструмент:
- Помещает сертификат в
~/.aspnet/dev-certs/trust
- Запускает упрощенную версию средства c_rehash OpenSSL в каталоге.
- Просит пользователя обновить
SSL_CERT_DIR
переменную среды.
Чтобы установить доверие к dotnet, средство помещает сертификат в My/Root
хранилище сертификатов.
Чтобы установить доверие к базам данных NSS , если таковые имеются, средство выполняет поиск в домашнем каталоге для нахождения профилей Firefox, ~/.pki/nssdb
и ~/snap/chromium/current/.pki/nssdb
. Для каждого найденного каталога средство добавляет запись в nssdb
.
Шаблоны обновлены до последних версий Bootstrap, jQuery и jQuery Validation
Шаблоны и библиотеки основных проектов ASP.NET были обновлены для использования последних версий Bootstrap, jQuery и jQuery Validation, в частности:
- Bootstrap 5.3.3
- jQuery 3.7.1
- Проверка jQuery 1.21.0
ASP.NET Core