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


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

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

Эта статья была обновлена для кандидата версии 2 .NET 9.

Blazor

В этом разделе описываются новые возможности.Blazor

.NET MAUIBlazor Hybrid Шаблон решения веб-приложения и

Новый шаблон решения упрощает создание .NET MAUI собственных и Blazor веб-клиентских приложений, использующих один и тот же пользовательский интерфейс. В этом шаблоне показано, как создавать клиентские приложения, которые позволяют максимально использовать код и использовать целевой объект Android, iOS, Mac, Windows и Web.

К ключевым функциям этого шаблона относятся:

  • Возможность выбора интерактивного 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приложения".

Оптимизация доставки статических ресурсов

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

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

Обнаружение расположения отрисовки, интерактивности и назначенного режима отрисовки во время выполнения

Мы представили новый API, предназначенный для упрощения процесса запроса состояний компонентов во время выполнения. Этот API предоставляет следующие возможности:

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

Дополнительные сведения см. в режимах отрисовки ASP.NET CoreBlazor.

Улучшена возможность повторного подключения на стороне сервера:

Следующие улучшения были улучшены в интерфейсе повторного подключения на стороне сервера по умолчанию:

  • Когда пользователь переходит обратно к приложению с отключенным каналом, повторное подключение выполняется немедленно, а не ожидает продолжительности следующего интервала повторного подключения. Это улучшает взаимодействие с пользователем при переходе к приложению на вкладке браузера, которая ушла в спящий режим.

  • Когда попытка повторного подключения достигает сервера, но сервер уже выпустил канал, происходит автоматическое обновление страницы. Это позволяет пользователю вручную обновить страницу, если она, скорее всего, приведет к успешному повторному подключению.

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

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

Эти поставщики перетекают состояние проверки подлинности пользователя в браузер. Проверка подлинности на сервере, а не клиент позволяет приложению получать доступ к состоянию проверки подлинности во время предварительной подготовки и до инициализации среды выполнения .NET WebAssembly.

Пользовательские AuthenticationStateProvider реализации используют службу состояния сохраняемого компонента (PersistentComponentState) для сериализации состояния проверки подлинности в примечаниях HTML и считывания его обратно из WebAssembly для создания нового AuthenticationState экземпляра.

Это работает хорошо, если вы начали работу с Blazor Web App шаблона проекта и выбрали параметр "Отдельные учетные записи ", но это много кода для реализации самостоятельно или копирования, если вы пытаетесь добавить проверку подлинности в существующий проект. Теперь существуют API- интерфейсы, которые теперь являются частью Blazor Web App шаблона проекта, которые можно вызывать в проектах сервера и клиента, чтобы добавить эту функцию:

  • AddAuthenticationStateSerialization: добавляет необходимые службы для сериализации состояния проверки подлинности на сервере.
  • AddAuthenticationStateDeserialization: добавляет необходимые службы для десериализации состояния проверки подлинности в браузере.

По умолчанию API сериализует только серверное имя и утверждения роли для доступа в браузере. Параметр можно передать для AddAuthenticationStateSerialization включения всех утверждений.

Дополнительные сведения см. в следующих разделах приложений Secure ASP.NET Core:Blazor

Добавление страниц отрисовки на стороне статического сервера (SSR) в глобальный интерактивный Blazor Web App

В выпуске .NET 9 теперь проще добавить статические страницы SSR в приложения, которые принимают глобальную интерактивность.

Этот подход полезен только в том случае, если приложение имеет определенные страницы, которые не могут работать с интерактивным сервером или отрисовкой WebAssembly. Например, используйте этот подход для страниц, которые зависят от чтения и записи файлов cookie HTTP и могут работать только в цикле запроса или ответа вместо интерактивной отрисовки. Для страниц, работающих с интерактивной отрисовкой, их не следует заставлять использовать статическую отрисовку SSR, так как это менее эффективно и менее гибко для конечного пользователя.

Пометьте любую Razor страницу компонента с новым [ExcludeFromInteractiveRouting] атрибутом, назначенным директивой @attributeRazor :

@attribute [ExcludeFromInteractiveRouting]

Применение атрибута приводит к переходу на страницу для выхода из интерактивной маршрутизации. Входящий переход принудительно выполняет перезагрузку полностраничной перезагрузки вместо разрешения страницы с помощью интерактивной маршрутизации. Перезагрузка полностраничной перезагрузки заставляет корневой компонент верхнего уровня( как правило App , компонент (App.razor) перенаправить с сервера, что позволяет приложению переключиться на другой режим отрисовки верхнего уровня.

Метод HttpContext.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;
}

Альтернативой использованию метода расширения является чтение метаданных конечной HttpContext.AcceptsInteractiveRouting точки вручную.HttpContext.GetEndpoint()?.Metadata

Эта функция рассматривается в справочной документации в режимах отрисовки ASP.NET CoreBlazor.

Внедрение конструктора

Razor компоненты поддерживают внедрение конструктора.

В следующем примере частичный класс (code-behind) внедряет NavigationManager службу с помощью основного конструктора:

public partial class ConstructorInjection(NavigationManager navigation)
{
    private void HandleClick()
    {
        navigation.NavigateTo("/counter");
    }
}

Дополнительные сведения см. в статье Внедрение зависимостей Blazor ASP.NET Core.

Сжатие Websocket для компонентов интерактивного сервера

По умолчанию компоненты интерактивного сервера обеспечивают сжатие для подключений WebSocket и задают frame-ancestors директиву 'self'"Политика безопасности содержимого" (CSP), которая позволяет внедрять приложение только в <iframe> источник, из которого приложение обслуживается при включении сжатия или при указании конфигурации контекста WebSocket.

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

.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)

Настройте более frame-ancestors строгий поставщик служб CSP со значением 'none' (требуется одинарные кавычки), что позволяет сжатию WebSocket, но не позволяет браузерам внедрять приложение в любое:<iframe>

.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")

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

Обработка событий композиции клавиатуры в 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; }
    }
}

Несколько Blazor Web Apps для каждого проекта сервера

Поддержка нескольких Blazor Web Apps на серверный проект будет рассматриваться для .NET 10 (ноябрь 2025 г.).

Дополнительные сведения см. в разделе "Поддержка нескольких Blazor веб-приложений на серверный проект" (dotnet/aspnetcore #52216).

SignalR

В этом разделе описываются новые возможности.SignalR

Поддержка полиморфного типа в SignalR Центрах

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

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 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 файл:

// 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())
        {
            // We want to view all traces in development
            tracing.SetSampler(new AlwaysOnSampler());
        }

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
    });

builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());

Ниже приведен пример выходных данных на панели мониторинга Aspire:

Список действий для SignalR событий вызова метода Концентратора

SignalR поддерживает обрезку и собственный AOT

Продолжая собственное путешествие 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.
  • На сервере SignalR параметры метода Концентратора типа IAsyncEnumerable<T> и ChannelReader<T> где T находится ЗначениеType (struct) не поддерживаются. Использование этих типов приводит к исключению среды выполнения при запуске в разработке и в опубликованном приложении. Дополнительные сведения см. в статье об использовании IAsyncEnumerable<T и ChannelReader<T>> с ValueTypes в собственном AOT (dotnet/aspnetcore#56179).SignalR
  • Строго типизированные концентраторы не поддерживаются в машинном коде AOT (PublishAot). Использование строго типизированных центров с машинным AOT приведет к предупреждению во время сборки и публикации, а также исключению среды выполнения. Использование строго типизированных концентраторов с обрезкой (PublishedTrimmed) поддерживается.
  • Только Task, Task<T>ValueTaskили ValueTask<T> поддерживаются для асинхронных возвращаемых типов.

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

В этом разделе описываются новые функции для минимальных API.

В TypedResults добавлены аргументы InternalServerError и InternalServerError<TValue>.

Класс 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);

OpenAPI

В этом разделе описываются новые возможности OpenAPI

Встроенная поддержка создания документов OpenAPI

Спецификация OpenAPI — это стандарт для описания API-интерфейсов HTTP. Стандарт позволяет разработчикам определять форму API, которые можно подключить к клиентским генераторам, генераторам серверов, средствам тестирования, документации и т. д. В .NET 9 Preview 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 --prerelease

Запустите приложение и перейдите к openapi/v1.json просмотру созданного документа OpenAPI:

Документ OpenAPI

Документы OpenAPI также можно создать во время сборки, добавив Microsoft.Extensions.ApiDescription.Server пакет:

dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease

В файле проекта приложения добавьте следующее:

<PropertyGroup>
  <OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
  <OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
</PropertyGroup>

Запустите dotnet build и проверьте созданный JSON-файл в каталоге проекта.

Создание документов OpenAPI во время сборки

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

Дополнительные сведения о новых возможностях документов OpenAPI в ASP.NET Core см . в новых документах Microsoft.AspNetCore.OpenApi.

Улучшения завершения Intellisense для пакета OpenAPI

ASP.NET поддержка OpenAPI Core теперь является более доступной и удобной для пользователей. API OpenAPI отправляются в виде независимого пакета, отдельно от общей платформы. До сих пор это означало, что разработчики не имели удобства функций завершения кода, таких как Intellisense для API OpenAPI.

Учитывая этот разрыв, мы ввели новый поставщик завершения и средство исправления кода. Эти средства предназначены для упрощения обнаружения и использования API OpenAPI, что упрощает интеграцию OpenAPI с проектами разработчиков. Поставщик завершения предлагает предложения кода в режиме реального времени, а средство исправления кода помогает исправить распространенные ошибки и улучшить использование API. Это улучшение является частью нашей постоянной приверженности улучшению интерфейса разработчика и упрощению рабочих процессов, связанных с API.

Когда пользователь вводит инструкцию, в которой доступен API, связанный с OpenAPI, поставщик завершения отображает рекомендацию по API. Например, на следующих снимках экрана завершения для AddOpenApi и MapOpenApi предоставляются при вводе пользователем инструкции вызова в поддерживаемом типе, например IEndpointConventionBuilder:

Завершение OpenAPI

Когда завершение принято и Microsoft.AspNetCore.OpenApi пакет не установлен, кодефиксер предоставляет ярлык для автоматической установки зависимостей в проекте.

Автоматическая установка пакета

[Required] Поддержка и [DefaultValue] атрибуты для параметров и свойств

Когда [Required] и [DefaultValue] атрибуты применяются к параметрам или свойствам в сложных типах, реализация OpenAPI сопоставляет их default required с свойствами в документе OpenAPI, связанном с схемой параметра или типа.

Например, следующий API создает сопровождающую схему для Todo типа.

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

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

app.MapPost("/todos", (Todo todo) => { });

app.Run();

class Todo
{
	public int Id { get; init; }
	public required string Title { get; init; }
	[DefaultValue("A new todo")]
	public required string Description { get; init; }
	[Required]
	public DateTime CreatedOn { get; init; }
}
{
	"required": [
	  "title",
	  "description",
	  "createdOn"
	],
	"type": "object",
	"properties": {
	  "id": {
	    "type": "integer",
	    "format": "int32"
	  },
	  "title": {
	    "type": "string"
	  },
	  "description": {
	    "type": "string",
	    "default": "A new todo"
	  },
	  "createdOn": {
	    "type": "string",
	    "format": "date-time"
	  }
	}
}

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

Встроенная поддержка OpenAPI теперь поставляется с поддержкой преобразователей схем, которые можно использовать для изменения схем, созданных System.Text.Json и реализацией OpenAPI. Как и преобразователи документов и операций, преобразователи схем могут быть зарегистрированы в объекте OpenApiOptions . Например, в следующем примере кода показано использование преобразователя схемы для добавления примера в схему типа.

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Any;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddSchemaTransformer((schema, context, cancellationToken) =>
    {
        if (context.JsonTypeInfo.Type == typeof(Todo))
        {
            schema.Example = new OpenApiObject
            {
                ["id"] = new OpenApiInteger(1),
                ["title"] = new OpenApiString("A short title"),
                ["description"] = new OpenApiString("A long description"),
                ["createdOn"] = new OpenApiDateTime(DateTime.Now)
            };
        }
        return Task.CompletedTask;
    });
});

var app = builder.Build();

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

app.MapPost("/todos", (Todo todo) => { });

app.Run();

class Todo
{
	public int Id { get; init; }
	public required string Title { get; init; }
	[DefaultValue("A new todo")]
	public required string Description { get; init; }
	[Required]
	public DateTime CreatedOn { get; init; }
}

Усовершенствования API регистрации преобразователя в Microsoft.AspNetCore.OpenApi

Преобразователи OpenAPI поддерживают изменение документа OpenAPI, операций в документе или схем, связанных с типами в API. API-интерфейсы для регистрации преобразователей в документе OpenAPI предоставляют различные варианты регистрации преобразователей.

Ранее для регистрации преобразователей были доступны следующие API:

OpenApiOptions AddDocumentTransformer(Func<OpenApiDocument, OpenApiDocumentTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddDocumentTransformer(IOpenApiDocumentTransformer transformer)
OpenApiOptions AddDocumentTransformer<IOpenApiDocumentTransformer>()
OpenApiOptions UseSchemaTransformer(Func<OpenApiSchema, OpenApiSchemaTransformerContext, CancellationToken, Task>)
OpenApiOptions AddOperationTransformer(Func<OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken, Task>)

Новый набор API выглядит следующим образом:

OpenApiOptions AddDocumentTransformer(Func<OpenApiDocument, OpenApiDocumentTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddDocumentTransformer(IOpenApiDocumentTransformer transformer)
OpenApiOptions AddDocumentTransformer<IOpenApiDocumentTransformer>()

OpenApiOptions AddSchemaTransformer(Func<OpenApiSchema, OpenApiSchemaTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddSchemaTransformer(IOpenApiSchemaTransformer transformer)
OpenApiOptions AddSchemaTransformer<IOpenApiSchemaTransformer>()

OpenApiOptions AddOperationTransformer(Func<OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddOperationTransformer(IOpenApiOperationTransformer transformer)
OpenApiOptions AddOperationTransformer<IOpenApiOperationTransformer>()

Microsoft.AspNetCore.OpenApi поддерживает обрезку и собственный AOT

Новая встроенная поддержка OpenAPI в ASP.NET Core теперь также поддерживает обрезку и собственный AOT.

Начало работы

Создайте проект ASP.NET Core Web API (Native AOT).

dotnet new webapiaot

Добавьте пакет Microsoft.AspNetCore.OpenAPI.

dotnet add package Microsoft.AspNetCore.OpenApi --prerelease

Для этой предварительной версии также необходимо добавить последний пакет Microsoft.OpenAPI, чтобы избежать обрезки предупреждений.

dotnet add package Microsoft.OpenApi

Обновите Program.cs , чтобы включить создание документов OpenAPI.

+ builder.Services.AddOpenApi();

var app = builder.Build();

+ app.MapOpenApi();

Публикация приложения.

dotnet publish

Приложение публикует собственный AOT без предупреждений.

Поддержка звонков ProducesProblem и ProducesValidationProblem групп маршрутов

Методы расширения ProducesProblem и ProducesValidationProblem были обновлены для групп маршрутов. Эти методы можно использовать для указания того, что все конечные точки в группе маршрутов могут возвращать ProblemDetails или ValidationProblemDetails ответы в целях метаданных OpenAPI.

var app = WebApplication.Create();

var todos = app.MapGroup("/todos")
    .ProducesProblem(StatusCodes.Status500InternalServerError);

todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));

app.Run();

record Todo(int Id, string Title, bool IsCompleted);

Problemи ValidationProblem типы результатов поддерживают построение со значениями IEnumerable<KeyValuePair<string, object?>>

До .NET 9 создание типов результатов проблемы и проверки в минимальных 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 за этот вклад!

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

В этом разделе описываются новые функции проверки подлинности и авторизации.

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 поддерживается рядом поставщиков identity , включая

Для .NET 9 мы решили включить PAR по умолчанию, если identity документ обнаружения поставщика объявляет поддержку PAR, так как он должен обеспечить расширенную безопасность для поставщиков, которые поддерживают его. identity Документ обнаружения поставщика обычно найден по адресу.well-known/openid-configuration. Если это вызывает проблемы, можно отключить PAR с помощью OpenIdConnectOptions.PushedAuthorizationBehavioror , как показано ниже.

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_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING флаги и HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL HTTP.sys с помощью новых EnableKerberosCredentialCaching и CaptureCredentials свойств в HTTP.sys AuthenticationManager для оптимизации обработки проверка подлинности Windows. Например:

webBuilder.UseHttpSys(options =>
{
    options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
    options.Authentication.EnableKerberosCredentialCaching = true;
    options.Authentication.CaptureCredentials = true;
});

Разное

В следующих разделах описаны другие возможности.

Новая HybridCache библиотека

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. Но даже без нее IDistributedCacheHybridCache служба по-прежнему будет обеспечивать кэширование в процессе и защиту от метки.

Примечание о повторном использованию объекта

В типичном существующем коде, который использует IDistributedCache, каждый извлечение объекта из кэша приводит к десериализации. Это означает, что каждый одновременный вызывающий объект получает отдельный экземпляр объекта, который не может взаимодействовать с другими экземплярами. Результатом является безопасность потоков, так как не существует риска параллельных изменений в одном экземпляре объекта.

Так как много использования будет адаптировано из существующего HybridCache IDistributedCache кода, сохраняет это поведение по умолчанию, HybridCache чтобы избежать возникновения ошибок параллелизма. Однако данный вариант использования по сути является потокобезопасной:

  • Если кэшируемые типы неизменяемы.
  • Если код не изменяет их.

В таких случаях сообщите HybridCache , что можно повторно использовать экземпляры следующим образом:

  • Пометка типа как sealed. Ключевое sealed слово в C# означает, что класс не может быть унаследован.
  • Применение атрибута [ImmutableObject(true)] к нему. Атрибут [ImmutableObject(true)] указывает, что состояние объекта невозможно изменить после его создания.

Повторно используя экземпляры, можно сократить затраты на выделение ЦП и объектов, HybridCache связанных с десериализацией каждого вызова. Это может привести к улучшению производительности в сценариях, когда кэшированные объекты являются большими или часто доступны.

Другие HybridCache функции

Например IDistributedCache, HybridCache поддерживает удаление по ключу RemoveKeyAsync с помощью метода.

HybridCache также предоставляет необязательные API для IDistributedCache реализаций, чтобы избежать byte[] выделения. Эта функция реализуется предварительными версиями и Microsoft.Extensions.Caching.SqlServer пакетамиMicrosoft.Extensions.Caching.StackExchangeRedis.

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

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

Оптимизация доставки статических веб-ресурсов

Для обслуживания статических ресурсов в рабочей среде требуется значительный объем работы и технических знаний. Без оптимизаций, таких как сжатие, кэширование и отпечатки пальцев:

  • Браузер должен выполнять дополнительные запросы на каждую загрузку страницы.
  • Более байтов, чем необходимо, передаются через сеть.
  • Иногда устаревшие версии файлов предоставляются клиентам.

Для создания выполняющихся веб-приложений требуется оптимизация доставки ресурсов в браузер. Возможные оптимизации:

  • Обслуживают указанный ресурс один раз, пока файл не изменится или браузер очищает его кэш. Задайте заголовок ETag.
  • Запретить браузеру использовать старые или устаревшие ресурсы после обновления приложения. Задайте заголовок Last-Modified.
  • Настройте правильные заголовки кэширования.
  • Используйте по промежуточному слоям кэширования.
  • По возможности обслуживайте сжатые версии ресурсов.
  • Используйте CDN для обслуживания ресурсов ближе к пользователю.
  • Свести к минимуму размер ресурсов, обслуживаемых браузером. Эта оптимизация не включает минификации.

MapStaticAssets — это новое ПО промежуточного слоя, которое помогает оптимизировать доставку статических ресурсов в приложении. Она предназначена для работы со всеми платформами пользовательского интерфейса, включая BlazorRazor страницы и 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 работает путем объединения процессов сборки и публикации для сбора сведений обо всех статических ресурсах в приложении. Затем эти сведения используются библиотекой среды выполнения для эффективного обслуживания этих файлов в браузере.

MapStaticAssetsUseStaticFiles в большинстве случаев он оптимизирован для обслуживания ресурсов, о которых приложение знает во время сборки и публикации. Если приложение обслуживает ресурсы из других расположений, таких как диск или внедренные ресурсы, UseStaticFiles следует использовать.

MapStaticAssets предоставляет следующие преимущества, с которыми UseStaticFilesне найдено:

  • Сжатие времени сборки для всех ресурсов в приложении:
    • gzip во время разработки и gzip + brotli во время публикации.
    • Все ресурсы сжимаются с целью уменьшения размера ресурсов до минимального.
  • На основе ETagsсодержимого: Etags для каждого ресурса используется строка в кодировке Base64 хэша SHA-256 содержимого. Это гарантирует, что браузер перезагрузит файл только в том случае, если его содержимое изменилось.

В следующей таблице показаны исходные и сжатые размеры CSS и JS файлов в шаблоне Pages по умолчанию Razor :

Файлы Исходная Compressed Сокращение %
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%

В следующей таблице показаны исходные и сжатые размеры с помощью библиотеки компонентов пользовательского интерфейса Blazor Fluent:

Файлы Исходная Compressed Сокращение %
плавный.js 384 73 80.99%
fluent.css 94 11 88.30%
Всего 478 84 82.43%

В общей сложности 478 КБ сжато до 84 КБ.

В следующей таблице показаны исходные и сжатые размеры с помощью библиотеки компонентов MudBlazorBlazor :

Файлы Исходная Compressed Сокращение
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:

Служба IIS gzip MapStaticAssets Сокращение MapStaticAssets
≅ 90 37,5 59 %

ASP0026: анализатор, предупреждая, когда [Авторизация] переопределяется [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 как в конструкторе, так и в методеInvokeAsyncInvoke/:

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, если таковые есть, средство выполняет поиск home каталога для профилей Firefox и ~/.pki/nssdb~/snap/chromium/current/.pki/nssdb. Для каждого найденного каталога средство добавляет в нее nssdbзапись.

Шаблоны обновлены до последних версий Bootstrap, jQuery и jQuery Validation

Шаблоны и библиотеки основных проектов ASP.NET были обновлены для использования последних версий Bootstrap, jQuery и jQuery Validation, в частности:

  • Начальная загрузка 5.3.3
  • jQuery 3.7.1
  • Проверка jQuery 1.21.0