Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. Для текущего выпуска см. статью версия .NET 9.
Важно!
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов. Каждый компонент:
определяет, нужно ли передать запрос следующему компоненту в конвейере;
может выполнять работу до и после следующего компонента в конвейере.
Для построения конвейера запросов используются делегаты запроса. Делегаты запросов обрабатывают каждый HTTP-запрос.
Делегаты запросов настраиваются с использованием методов расширения Run, Map и Use. Отдельный делегат запроса можно указать в виде встроенного анонимного метода (называемого посредником) либо определить в переиспользуемом классе. Эти многоразовые классы и встроенные анонимные методы являются промежуточным ПО или компонентами промежуточного слоя. Каждый компонент ПО промежуточного слоя в конвейере запросов отвечает за вызов следующего компонента в конвейере или замыкает конвейер. Когда промежуточный слой прерывает выполнение, он называется терминальным промежуточным слоем, так как препятствует обработке запроса другими промежуточными слоями.
Blazor Web App, Razor Pages и MVC обрабатывают запросы браузера на сервере посредством промежуточного слоя. Инструкции, приведенные в этой статье, относятся к этим типам приложений.
Автономные Blazor WebAssembly приложения выполняются полностью на клиенте и не обрабатывают запросы с использованием промежуточного ПО. Руководство, приведенное в этой статье, не относится к автономным приложениям Blazor WebAssembly.
Анализ кода ПО промежуточного слоя
ASP.NET Core содержит множество анализаторов .NET Compiler Platform, проверяющих качество кода приложения. Дополнительные сведения см. в статье Анализ кода в приложениях ASP.NET Core.
Создайте middleware-конвейер с помощью WebApplication
Конвейер запросов ASP.NET Core состоит из последовательности делегатов запроса, вызываемых один за другим. На следующей схеме демонстрируется этот принцип. Поток выполнения показан черными стрелками.
Каждый из делегатов может выполнять операции до и после следующего делегата. Делегаты обработки исключений должны вызываться в начале конвейера, чтобы перехватывать исключения, возникающие на более поздних этапах.
Простейшее приложение ASP.NET Core задает один делегат запроса, обрабатывающий все запросы. В этом случае конвейер запросов как таковой отсутствует. Вместо этого в ответ на каждый HTTP-запрос вызывается одна анонимная функция.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
Несколько делегатов запроса можно соединить в цепочку с помощью Use. Параметр next представляет следующий делегат в конвейере. Вы можете прервать выполнение конвейера, не вызывая функцию next. Обычно действия можно выполнять как до, так и после next делегата, как показано в этом примере:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Прерывание обработки конвейера запросов
Если делегат не передает запрос следующему делегату, это называется замыканием конвейера запросов. Короткое замыкание часто является предпочтительным, так как позволяет избежать ненужной работы. Например, промежуточное ПО для статических файлов может выступать в качестве завершающего промежуточного ПО, обрабатывая запрос к статическому файлу и прерывая остальную часть конвейера. Компоненты промежуточного слоя, добавленные в конвейер перед промежуточным слоем, завершающим обработку, продолжают обработку кода после своих команд next.Invoke. Примите во внимание следующее предупреждение о попытке записи в ответ, который уже был отправлен.
Делегаты Run не получают параметр next. Первый делегат Run всегда является конечным и завершает конвейер.
Run — это соглашение. Некоторые компоненты промежуточного слоя могут предоставлять методы Run[Middleware], которые выполняются в конце конвейера:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
В предыдущем примере делегат Run записывает "Hello from 2nd delegate." в ответ и завершает конвейер. Если добавить другой делегат Use или Run после делегата Run, он не будет вызван.
Рекомендуется использовать перегрузку app.Use, требующую передачи контекста в следующий компонент.
На следующей схеме показан полный конвейер обработки запросов для приложений ASP.NET Core MVC и Razor Pages. Вы можете увидеть, как в стандартном приложении упорядочены существующие программы промежуточного слоя и где добавляются пользовательские программы промежуточного слоя. Вы можете полностью контролировать изменение порядка существующего ПО промежуточного слоя или внедрять новое пользовательское ПО промежуточного слоя для своих сценариев.
Промежуточное ПО конечной точки на предыдущей схеме выполняет конвейер фильтра для соответствующего типа приложения — MVC или страниц.
Промежуточное ПО Маршрутизации на вышеуказанной схеме размещено после Статических файлов. В таком порядке реализуются шаблоны проектов путем явного вызова app.UseRouting. Если вы не вызываете app.UseRouting, то промежуточное ПО маршрутизации по умолчанию запускается в начале конвейера. Дополнительные сведения см. в разделе Маршрутизация.
Порядок добавления компонентов ПО промежуточного слоя в Program.cs файл определяет порядок вызова компонентов ПО промежуточного слоя для запросов и обратного порядка ответа. Соблюдать этот порядок крайне важно для обеспечения безопасности, производительности и функциональности.
Следующий выделенный код в Program.cs добавляет компоненты промежуточного слоя, связанные с безопасностью, в типично рекомендуемом порядке.
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
UseRequestLocalization должен отображаться перед любым промежуточным ПО, которое может проверить язык и региональные параметры запроса, например app.UseStaticFiles().
UseRateLimiter необходимо вызывать после UseRouting, когда используются API ограничения скорости для конкретных конечных точек. Например, если используется атрибут [EnableRateLimiting], необходимо вызвать UseRateLimiter после UseRouting. При вызове только глобальных лимитаторов UseRateLimiter можно вызвать раньше UseRouting.
В некоторых сценариях ПО промежуточного слоя имеет другой порядок. Например, порядок кэширования и сжатия зависит от сценария, и существует несколько допустимых вариантов такого порядка. Например:
С помощью приведенного выше кода можно снизить загрузку ЦП путем кэширования сжатого ответа, но при этом может быть выполнено кэширование нескольких представлений ресурса с помощью разных алгоритмов сжатия, таких как Gzip или Brotli.
В приведенном ниже порядке объединяются статические файлы, чтобы разрешить кэширование сжатых статических файлов.
Промежуточное ПО для перенаправления HTTPS (UseHttpsRedirection) перенаправляет запросы с HTTP на HTTPS.
Промежуточное ПО для статических файлов (UseStaticFiles) возвращает статические файлы и прерывает дальнейшую обработку запросов.
Программное обеспечение Middleware Cookie (UseCookiePolicy) обеспечивает соответствие приложения требованиям Общего регламента по защите данных (GDPR) ЕС.
Промежуточное ПО маршрутизации (UseRouting) для маршрутизации запросов.
ПО промежуточного слоя проверки подлинности (UseAuthentication) пытается проверить подлинность пользователя, прежде чем предоставить ему доступ к защищенным ресурсам.
ПО промежуточного слоя авторизации (UseAuthorization) разрешает пользователю доступ к защищенным ресурсам.
Промежуточное ПО сеанса (UseSession) устанавливает и поддерживает состояние сеанса. Если в приложении используется состояние сеанса, вызовите промежуточное ПО сеанса после промежуточного ПО политики Cookie и до промежуточного ПО MVC.
Промежуточное ПО маршрутизации конечных точек (UseEndpoints с MapRazorPages) для добавления конечных точек страниц Razor в конвейер запросов.
UseExceptionHandler — это первый компонент промежуточного слоя, добавленный в конвейер. Таким образом, промежуточное ПО для обработки исключений перехватывает все исключения, возникающие в последующих вызовах.
Промежуточный компонент статических файлов вызывается на ранних этапах конвейера. Это позволяет ему обрабатывать запросы и прекратить обработку, минуя остальные компоненты. Этот компонент статических файлов не выполняет проверки авторизации. Все файлы, обслуживаемые промежуточным программным обеспечением для статических файлов, включая те, что находятся в wwwroot, находятся в открытом доступе. Сведения о защите статических файлов см. в статье Статические файлы в ASP.NET Core.
Если запрос не обрабатывается компонентом промежуточного слоя для статических файлов, он передается в компонент промежуточного слоя для проверки подлинности (UseAuthentication), который выполняет проверку подлинности. Аутентификация не прерывает необработанные запросы. Хотя промежуточное программное обеспечение для аутентификации проверяет подлинность запросов, авторизация (и отклонение) выполняются только после того, как MVC выберет указанную страницу или контроллер MVC и action.
Следующий пример демонстрирует порядок работы компонентов промежуточного ПО, в котором запросы на статические файлы обрабатываются компонентом для статических файлов до компонента для сжатия ответов. Статические файлы не сжимаются при таком порядке промежуточного программного обеспечения. Ответы Razor Pages могут быть сжаты.
Порядок вызовов UseCors и UseStaticFiles зависит от приложения. Дополнительные сведения см. в разделе Порядок UseCors и UseStaticFiles.
Порядок промежуточного программного обеспечения для пересылаемых заголовков
Промежуточный слой для перенаправления заголовков должен выполняться перед другими промежуточными слоями. Такой порядок гарантирует, что промежуточное программное обеспечение, полагающееся на сведения о перенаправленных заголовках, может использовать значения заголовков для обработки. Чтобы запустить ПО перенаправления заголовков после ПО для диагностики и обработки ошибок, см. Порядок ПО перенаправления заголовков.
Разветвить конвейер промежуточного ПО
Расширения Map используются как условность для ветвления конвейера.
Map разветвляет конвейер обработки запросов на основе совпадений с заданным путем запроса. Если путь запроса начинается с заданного пути, данная ветвь выполняется.
Map также может сопоставить несколько сегментов одновременно:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen осуществляет ветвление конвейера запросов на основе результата заданного предиката. Любой предикат типа Func<HttpContext, bool> можно использовать для сопоставления запросов с новой ветвью конвейера. В следующем примере предикат служит для определения наличия переменной строки запроса branch.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
Ниже приведены запросы и отклики http://localhost:1234 на базе предыдущего кода.
Запрос
Ответ
localhost:1234
Hello from non-Map delegate.
localhost:1234/?branch=main
Branch used = main
UseWhen также осуществляет ветвление конвейера запросов на основе результата заданного предиката. В отличие от MapWhen, эта ветвь будет повторно присоединена к основной магистрали, если она не содержит промежуточный терминальный уровень.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
В предыдущем примере ответ Hello from non-Map delegate. записывается для всех запросов. Если запрос включает переменную строки запроса branch, ее значение регистрируется до того, как будет выполнено повторное объединение с основным конвейером.
Встроенное посредническое ПО
ASP.NET Core содержит следующие компоненты промежуточного слоя. В столбце Порядок указаны сведения о размещении ПО промежуточного слоя в конвейере обработки запросов и условия, в соответствии с которыми ПО промежуточного слоя может прервать обработку запроса. Если промежуточный слой замыкает конвейер обработки запроса и препятствует обработке запроса дальнейшими компонентами промежуточного слоя, он называется терминальным промежуточным слоем. Дополнительные сведения о коротком замыкании см. в разделе «Создание конвейера ПО промежуточного слоя с WebApplication».
Обеспечивает отслеживание согласия пользователей на хранение личных сведений и соблюдение минимальных стандартов для полей cookie, таких как secure и SameSite.
** Перед промежуточным ПО, которое выдает файлы cookie. Например: Authentication, Session, MVC (TempData).
Создает страницу со сведениями об ошибках, предназначенными для использования только в среде разработки.
Ставится перед компонентами, выдающими ошибки. Шаблоны проектов автоматически регистрируют это ПО промежуточного слоя в качестве первого в конвейере в среде разработки.
Отдельное ПО промежуточного слоя, которое обеспечивает обработку исключений, предоставляет страницу исключений для разработчика, страницы состояния кода, веб-страницу по умолчанию для новых приложений.
Ставится перед компонентами, выдающими ошибки. Конечный пункт для обработки исключений или отображения веб-страницы по умолчанию для новых приложений.
Предоставляет поддержку кэширования ответов на основе конфигурации.
Ставится перед компонентами, требующими кэширование.
UseRouting нужно использовать перед UseOutputCaching.
UseCORS нужно использовать перед UseOutputCaching.
Обеспечивает поддержку для кэширования откликов. Для этого требуется участие клиента в работе. Используйте кэширование выходных данных для полного управления сервером.
Ставится перед компонентами, требующими кэширования.
UseCORS нужно использовать перед UseResponseCaching. Обычно не подходит для приложений пользовательского интерфейса, таких как Pages, так как Razor браузеры обычно задают заголовки запросов, которые предотвращают кэширование.
Кэширование выходных данных обеспечивает преимущества приложений пользовательского интерфейса.
Перед компонентами, чувствительными к локализации. Должно появляться после промежуточного ПО маршрутизации при использовании RouteDataRequestCultureProvider.
Перед компонентами, которым нужно принимать запросы WebSocket.
Дополнительные ресурсы
Параметры времени существования и регистрации содержат полный пример посреднического ПО со службами, имеющими время существования scoped (с заданной областью), transient (временное) и singleton (одиночное).
ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов. Каждый компонент:
определяет, нужно ли передать запрос следующему компоненту в конвейере;
может выполнять работу как до, так и после вызова следующего компонента в конвейере.
Для построения конвейера запросов используются делегаты запроса. Доверенные обработчики обрабатывают каждый HTTP-запрос.
Методы расширения Run, Map и Use используются для настройки запросов делегатов. Отдельный делегат запроса можно указать встроенным в качестве анонимного метода (называемого встроенным middleware), либо определить в классе, пригодном для повторного использования. Эти многоразовые классы и встроенные анонимные методы являются посредни́ковым программным обеспечением, также называемым компонентами посредникового ПО. Каждый компонент ПО промежуточного слоя в конвейере запросов отвечает за вызов следующего компонента в конвейере или замыкает конвейер. Когда промежуточный слой замыкает конвейер, он становится терминальным промежуточным слоем, так как препятствует обработке запроса дальнейшими компонентами промежуточного слоя.
Razor Pages, MVC, Blazor Serverи серверный проект размещенного решения Blazor WebAssembly обрабатывают запросы браузера на сервере с использованием промежуточного слоя. Инструкции, приведенные в этой статье, относятся к этим типам приложений.
Автономные Blazor WebAssembly приложения выполняются полностью на клиенте и не обрабатывают запросы с использованием промежуточного ПО. Руководство, приведенное в этой статье, не относится к автономным приложениям Blazor WebAssembly.
Анализ кода ПО промежуточного слоя
ASP.NET Core содержит множество анализаторов .NET Compiler Platform, проверяющих качество кода приложения. Дополнительные сведения см. в статье Анализ кода в приложениях ASP.NET Core.
Создайте конвейер промежуточного программного обеспечения с помощью WebApplication
Конвейер запросов ASP.NET Core состоит из последовательности делегатов запроса, вызываемых один за другим. На следующей схеме демонстрируется этот принцип. Поток выполнения показан черными стрелками.
Каждый из делегатов может выполнять операции до и после следующего делегата. Делегаты обработки исключений должны вызываться в начале конвейера, чтобы перехватывать исключения, возникающие на более поздних этапах.
Простейшее приложение ASP.NET Core задает один делегат запроса, обрабатывающий все запросы. В этом случае конвейер запросов как таковой отсутствует. Вместо этого в ответ на каждый HTTP-запрос вызывается одна анонимная функция.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
Можно объединить несколько делегатов запроса в цепочку с помощью Use. Параметр next представляет следующий делегат в конвейере. Замкнуть конвейер можно не вызывая параметр next. Обычно можно выполнять действия как до, так и после next делегата, как демонстрирует следующий пример:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Если делегат не передает запрос следующему делегату, это называется замыканием конвейера запросов. Частое использование короткого замыкания является предпочтительным, так как оно позволяет избежать ненужной работы. Например, промежуточное ПО для статических файлов может выступать в качестве завершающего промежуточного ПО, обрабатывая запрос к статическому файлу и прерывая остальную часть конвейера. Компоненты промежуточного слоя, добавленные в конвейер перед терминальным промежуточным слоем, которое прекращает дальнейшую обработку, продолжают обрабатывать код после своих инструкций next.Invoke. Однако обратите внимание на следующее предупреждение о попытке записи в отклик, который уже был отправлен.
может вызвать нарушение протокола, например, при записи более установленного значения Content-Length;
может привести к нарушению формата тела. например, при записи нижнего колонтитула HTML в CSS-файл.
HasStarted удобно использовать для обозначения того, были ли отправлены заголовки или записано в тело сообщения.
Делегаты Run не получают параметр next. Первый делегат Run всегда является конечным и завершает конвейер.
Run является соглашением. Некоторые компоненты промежуточного слоя могут предоставлять методы Run[Middleware], которые выполняются в конце конвейера:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
В предыдущем примере делегат Run записывает "Hello from 2nd delegate." в ответ и завершает конвейер. Если после делегата Run будет добавлен другой делегат Use или Run, он не будет вызван.
Предпочтительна перегрузка метода app.Use, требующая передачи контекста для дальнейшей обработки
На следующей схеме показан полный конвейер обработки запросов для приложений ASP.NET Core MVC и Razor Pages. Вы можете увидеть, как в стандартном приложении размещены существующие промежуточные слои и где добавляются пользовательские промежуточные слои. Вы имеете полный контроль над тем, как изменить порядок существующих промежуточных ПО или внедрить новые пользовательские промежуточные ПО в зависимости от ваших сценариев.
Промежуточное программное обеспечение конечной точки на предыдущей схеме выполняет конвейер фильтров для соответствующего типа приложения — MVC или Razor Страницы.
Промежуточное ПО маршрутизации изображено на данной схеме после статических файлов. В таком порядке реализуются шаблоны проектов путем явного вызова app.UseRouting. Если оператор app.UseRouting не вызван, по умолчанию ПО промежуточного слоя маршрутизации запускается в начале конвейера. Дополнительные сведения см. в разделе Маршрутизация.
Порядок добавления компонентов ПО промежуточного слоя в Program.cs файл определяет порядок вызова компонентов ПО промежуточного слоя для запросов и обратного порядка ответа. Соблюдать этот порядок крайне важно для обеспечения безопасности, производительности и функциональности.
Добавленный ниже выделенный код добавляет компоненты промежуточного слоя, связанные с безопасностью, в типичном рекомендуемом порядке в Program.cs.
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
UseRequestLocalization должен быть размещён перед любым промежуточным ПО, которое может проверить язык и региональные параметры запроса, например app.UseStaticFiles().
UseRateLimiter необходимо вызывать после UseRouting, когда используются API ограничения скорости для конкретной конечной точки. Например, если используется атрибут [EnableRateLimiting], UseRateLimiter необходимо вызвать после UseRouting. При вызове только глобальных ограничений UseRateLimiter можно вызвать раньше UseRouting.
В некоторых сценариях ПО промежуточного слоя имеет другой порядок. Например, порядок кэширования и сжатия зависит от сценария, и существует несколько допустимых вариантов такого порядка. Например:
С помощью приведенного выше кода можно снизить загрузку ЦП путем кэширования сжатого ответа, но при этом может быть выполнено кэширование нескольких представлений ресурса с помощью разных алгоритмов сжатия, таких как Gzip или Brotli.
В приведенном ниже порядке объединяются статические файлы, чтобы разрешить кэширование сжатых статических файлов.
Program.cs Следующий код добавляет компоненты ПО промежуточного слоя для распространенных сценариев приложений:
Обработка исключений и ошибок
При запуске приложения в среде разработки:
ПО промежуточного слоя страницы исключений для разработчика (UseDeveloperExceptionPage) сообщает об ошибках среды выполнения приложения.
Промежуточное программное обеспечение для страницы ошибок базы данных (UseDatabaseErrorPage) сообщает об ошибках среды выполнения базы данных.
При запуске приложения в рабочей среде:
ПО промежуточного слоя обработчика исключений (UseExceptionHandler) перехватывает исключения, возникающие в последующих промежуточных слоях.
ПО промежуточного слоя протокола HTTP Strict Transport Security Protocol (HSTS) (UseHsts) добавляет заголовок Strict-Transport-Security.
Промежуточное ПО для перенаправления HTTPS (UseHttpsRedirection) перенаправляет HTTP-запросы на HTTPS.
Промежуточное ПО для статических файлов (UseStaticFiles) возвращает статические файлы и прерывает дальнейшую обработку запросов.
Политический промежуточный слой Cookie (UseCookiePolicy) гарантирует соответствие приложения требованиям Общего регламента по защите данных (GDPR) ЕС.
Промежуточное ПО (UseRouting) для маршрутизации запросов.
ПО промежуточного слоя проверки подлинности (UseAuthentication) пытается проверить подлинность пользователя, прежде чем предоставить ему доступ к защищенным ресурсам.
ПО промежуточного слоя авторизации (UseAuthorization) разрешает пользователю доступ к защищенным ресурсам.
Промежуточное ПО сеансов (UseSession) устанавливает и поддерживает состояние сеанса. Если в приложении используется состояние сеанса, вызовите ПО промежуточного слоя сеанса после ПО промежуточного слоя политики Cookie и до ПО промежуточного слоя MVC.
Промежуточное ПО маршрутизации конечных точек (UseEndpoints и MapRazorPages) для добавления конечных точек страниц Razor в конвейер запросов.
UseExceptionHandler — это первый компонент промежуточного слоя, добавленный в конвейер. Таким образом, посредник по обработке исключений перехватывает все исключения, возникающие в последующих вызовах.
Промежуточный слой обработки статических файлов вызывается на раннем этапе конвейера, чтобы обрабатывать запросы и остановить процесс без прохождения через остальные компоненты. Этот компонент не выполняет проверки авторизации. Все обрабатываемые им файлы, включая расположенные в wwwroot, находятся в открытом доступе. Сведения о защите статических файлов см. в статье Статические файлы в ASP.NET Core.
Если запрос не обрабатывается компонентом промежуточного слоя для статических файлов, он передается в компонент промежуточного слоя для проверки подлинности (UseAuthentication), который выполняет проверку подлинности. Аутентификация не сбивает с толку неаутентифицированные запросы. Хотя промежуточное ПО для аутентификации проверяет аутентифицированность запросов, авторизация (и отклонение) выполняются только после того, как MVC выберет конкретную Razor Pages или контроллер MVC и действие.
Следующий пример демонстрирует порядок, при котором запросы статических файлов обрабатываются с помощью Static File Middleware перед Middleware сжатия ответов. Статические файлы не сжимаются в этом порядке промежуточного программного обеспечения. Ответы Razor Pages могут быть сжаты.
Дополнительные сведения об одностраничных приложениях см. в руководствах по шаблонам проектов React и Angular.
Порядок UseCors и UseStaticFiles
Порядок вызовов UseCors и UseStaticFiles зависит от приложения. Дополнительные сведения см. в разделе Порядок UseCors и UseStaticFiles.
Порядок промежуточного ПО перенаправления заголовков
Промежуточное ПО перенаправления заголовков должно выполняться перед другими промежуточными слоями. Такой порядок гарантирует, что промежуточное ПО, полагающееся на информацию о пересланных заголовках, может использовать значения заголовков для обработки. Для выполнения промежуточного слоя перенаправления заголовков после промежуточного слоя диагностики и обработки ошибок, см. Порядок промежуточного слоя перенаправления заголовков.
Ветвление конвейера ПО промежуточного слоя
Расширения Map используются как стандарт для ветвления конвейера.
Map осуществляет ветвление конвейера запросов на основе совпадений заданного пути запроса. Если путь запроса начинается с заданного пути, данная ветвь выполняется.
Map также может сопоставить несколько сегментов одновременно:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen осуществляет ветвление конвейера запросов на основе результата заданного предиката. Любой предикат типа Func<HttpContext, bool> можно использовать для сопоставления запросов с новой ветвью конвейера. В следующем примере предикат служит для определения наличия переменной строки запроса branch.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
Ниже приведены запросы и отклики http://localhost:1234 на базе предыдущего кода.
Запрос
Ответ
localhost:1234
Hello from non-Map delegate.
localhost:1234/?branch=main
Branch used = main
UseWhen также осуществляет ветвление конвейера запросов на основе результата заданного предиката. В отличие от MapWhen, эта ветвь снова присоединяется к основному конвейеру, если она не делает короткое замыкание или не содержит терминальный промежуточный слой.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
В предыдущем примере ответ Hello from non-Map delegate. записывается для всех запросов. Если запрос включает переменную строки запроса branch, ее значение регистрируется до того, как будет выполнено повторное объединение с основным конвейером.
Встроенное промежуточное ПО
ASP.NET Core содержит следующие компоненты промежуточного слоя. В столбце Порядок указаны сведения о размещении ПО промежуточного слоя в конвейере обработки запросов и условия, в соответствии с которыми ПО промежуточного слоя может прервать обработку запроса. Если промежуточный слой замыкает конвейер обработки запроса и препятствует обработке запроса дальнейшими компонентами промежуточного слоя, он называется терминальным промежуточным слоем. Дополнительные сведения о коротком замыкании см. в разделе "Создание конвейера ПО промежуточного слоя с помощью WebApplication".
Позволяет отслеживать согласие пользователей на хранение личных сведений и применять минимальные стандарты для полей cookie, таких как secure и SameSite.
До промежуточного ПО, которое выдает файлы cookie. Например: Authentication, Session, MVC (TempData).
Создает страницу со сведениями об ошибках, предназначенными для использования только в среде разработки.
Ставится перед компонентами, выдающими ошибки. Шаблоны проектов автоматически регистрируют это ПО промежуточного слоя в качестве первого в конвейере в среде разработки.
Отдельное ПО промежуточного слоя, которое обеспечивает обработку исключений, предоставляет страницу исключений для разработчика, страницы состояния кода, веб-страницу по умолчанию для новых приложений.
Ставится перед компонентами, выдающими ошибки. Терминал для обработки исключений или поддержка отображения веб-страницы по умолчанию для новых приложений.
Предоставляет поддержку кэширования ответов на основе конфигурации.
Ставится перед компонентами, требующими кэширование.
UseRouting нужно использовать перед UseOutputCaching.
UseCORS нужно использовать перед UseOutputCaching.
Обеспечивает поддержку для кэширования откликов. Для этого требуется участие клиента в работе. Используйте кэширование выходных данных для полного управления сервером.
Устанавливается перед компонентами, требующими кэширования.
UseCORS нужно использовать перед UseResponseCaching. Обычно не подходит для приложений пользовательского интерфейса, таких как Pages, так как Razor браузеры обычно задают заголовки запросов, которые предотвращают кэширование.
Кэширование выходных данных обеспечивает преимущества приложений пользовательского интерфейса.
Обрабатывает все запросы с этой точки в цепочке промежуточного программного обеспечения, возвращая страницу по умолчанию для одностраничного приложения (SPA)
В конце цепочки, чтобы другое ПО промежуточного слоя для обслуживания статических файлов, действий MVC и т. д. имело приоритет.
Перед компонентами, которые должны принимать запросы WebSocket.
Дополнительные ресурсы
Параметры времени существования и регистрации включают в себя полный пример промежуточного программного обеспечения со службами с привязкой к области, временными и отдельными периодами существования.
ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов. Каждый компонент:
определяет, нужно ли передать запрос следующему компоненту в конвейере;
может выполнять работу как до, так и после следующего компонента в конвейере.
Для построения конвейера запросов используются делегаты запроса. Делегаты запроса обрабатывают каждый HTTP-запрос.
Запросы делегатов настраиваются с помощью методов расширения Run, Map и Use. Отдельный делегат запроса можно указать непосредственно в виде анонимного метода (называемого встроенным промежуточным ПО) либо определить в повторно используемом классе. Эти многоразовые классы и встроенные анонимные методы являются промежуточным ПО или промежуточными компонентами. Каждый компонент ПО промежуточного слоя в конвейере запросов отвечает за вызов следующего компонента в конвейере или замыкает конвейер. Когда промежуточный слой прерывает выполнение, он становится терминальным промежуточным слоем, так как препятствует обработке запроса другими промежуточными слоями.
ASP.NET Core содержит множество анализаторов .NET Compiler Platform, проверяющих качество кода приложения. Дополнительные сведения см. в статье Анализ кода в приложениях ASP.NET Core.
Создание конвейера промежуточного ПО с помощью WebApplication
Конвейер запросов ASP.NET Core состоит из последовательности делегатов запроса, вызываемых один за другим. На следующей схеме демонстрируется этот принцип. Поток выполнения показан черными стрелками.
Каждый из делегатов может выполнять операции до и после следующего делегата. Делегаты обработки исключений должны вызываться в начале конвейера, чтобы перехватывать исключения, возникающие на более поздних этапах.
Простейшее приложение ASP.NET Core задает один делегат запроса, обрабатывающий все запросы. В этом случае конвейер запросов как таковой отсутствует. Вместо этого в ответ на каждый HTTP-запрос вызывается одна анонимная функция.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
Несколько делегатов запроса можно соединить в цепочку с помощью Use. Параметр next представляет следующий делегат в конвейере. Замкнуть конвейер можно не вызывая параметр next. Обычно можно выполнять действия как до делегата next, так и после него, как показано в этом примере.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Если делегат не передает запрос следующему делегату, это называется замыканием конвейера запросов. Короткозамыкание часто является предпочтительным, так как позволяет избежать ненужной работы. Например, промежуточное ПО для статических файлов может выступать в качестве завершающего промежуточного ПО, обрабатывая запрос к статическому файлу и прерывая остальную часть конвейера. Компоненты промежуточного слоя, добавленные в конвейер до промежуточного слоя, завершающего дальнейшую обработку, по-прежнему обрабатывают код после своих выражений next.Invoke. Но учитывайте следующее предупреждение о попытке записи в ответ, который уже был отправлен.
может вызвать нарушение протокола, Например, если написать больше, чем указано Content-Length.
может повредить формат тела. например, при записи нижнего колонтитула HTML в CSS-файл.
HasStarted удобно использовать для обозначения того, были ли отправлены заголовки или выполнена запись в тело отклика.
Делегаты Run не получают параметр next. Первый делегат Run всегда является конечным и завершает конвейер.
Run является соглашением. Некоторые компоненты промежуточного слоя могут предоставлять методы Run[Middleware], которые выполняются в конце конвейера:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
В предыдущем примере делегат Run записывает "Hello from 2nd delegate." в ответ и завершает конвейер. Если добавить другой делегат Use или Run после делегата Run, он не будет вызван.
Рекомендуется использовать перегрузку app.Use, которая требует передачи контекста в следующий обработчик.
Метод расширения app.Use без создания дополнительных объектов:
Требует передачи контекста в next.
Экономит два внутренних выделения памяти на один запрос, которые необходимы при использовании другой перегрузки.
На следующей схеме показан полный конвейер обработки запросов для приложений ASP.NET Core MVC и Razor Pages. Вы можете увидеть, как в типичном приложении расположены существующие промежуточные ПО и где добавляются пользовательские промежуточные ПО. Вы имеете полный контроль над тем, как изменить порядок существующих промежуточных программ или добавить новые пользовательские промежуточные программы в соответствии с вашими сценариями.
Промежуточное ПО конечной точки на предыдущей схеме выполняет фильтрационный конвейер для соответствующего типа приложения — MVC или Razor страницы.
Промежуточное ПО маршрутизации на приведенной выше схеме размещено после статических файлов. В таком порядке реализуются шаблоны проектов путем явного вызова app.UseRouting. Если оператор app.UseRouting не вызван, по умолчанию ПО промежуточного слоя маршрутизации запускается в начале конвейера. Дополнительные сведения см. в разделе Маршрутизация.
Порядок добавления компонентов ПО промежуточного слоя в Program.cs файл определяет порядок вызова компонентов ПО промежуточного слоя для запросов и обратного порядка ответа. Соблюдать этот порядок крайне важно для обеспечения безопасности, производительности и функциональности.
Следующий выделенный код в Program.cs добавляет компоненты промежуточного слоя, связанные с безопасностью, в типичном рекомендованном порядке.
using IndividualAccountsExample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
UseRequestLocalization нужно использовать перед любым ПО промежуточного слоя, которое может проверять язык и региональные параметры запроса (например, app.UseMvcWithDefaultRoute()).
В некоторых сценариях ПО промежуточного слоя имеет другой порядок. Например, порядок кэширования и сжатия зависит от сценария, и существует несколько допустимых вариантов такого порядка. Например:
С помощью приведенного выше кода можно снизить загрузку ЦП путем кэширования сжатого ответа, но при этом может быть выполнено кэширование нескольких представлений ресурса с помощью разных алгоритмов сжатия, таких как Gzip или Brotli.
В приведенном ниже порядке объединяются статические файлы, чтобы разрешить кэширование сжатых статических файлов.
Program.cs Следующий код добавляет компоненты ПО промежуточного слоя для распространенных сценариев приложений:
Обработка исключений и ошибок
При запуске приложения в среде разработки:
Промежуточное ПО страницы исключений разработчика (UseDeveloperExceptionPage) обнаруживает и сообщает об ошибках в среде выполнения приложения.
Промежуточное ПО для страницы ошибок базы данных (UseDatabaseErrorPage) сообщает об ошибках среды выполнения базы данных.
При запуске приложения в рабочей среде:
Промежуточное ПО для обработки исключений (UseExceptionHandler) перехватывает исключения, возникающие в следующих промежуточных ПО.
Промежуточное ПО HTTP Strict Transport Security (HSTS) (UseHsts) добавляет заголовок Strict-Transport-Security.
Промежуточное ПО перенаправления HTTPS (UseHttpsRedirection) перенаправляет запросы с HTTP на HTTPS.
Промежуточное ПО для работы со статическими файлами (UseStaticFiles) возвращает статические файлы и прерывает дальнейшую обработку запросов.
Промежуточное программное обеспечение политики (Cookie) (UseCookiePolicy) обеспечивает соответствие приложения требованиям Общего регламента защиты данных (GDPR) ЕС.
Промежуточное ПО для маршрутизации запросов (UseRouting).
ПО промежуточного слоя проверки подлинности (UseAuthentication) пытается проверить подлинность пользователя, прежде чем предоставить ему доступ к защищенным ресурсам.
ПО промежуточного слоя авторизации (UseAuthorization) разрешает пользователю доступ к защищенным ресурсам.
Промежуточное ПО для сеансов (UseSession) устанавливает и поддерживает состояние сеанса. Если в приложении используется состояние сеанса, вызовите ПО промежуточного слоя сеанса после ПО промежуточного слоя политики Cookie и до ПО промежуточного слоя MVC.
Промежуточное ПО для маршрутизации конечных точек (UseEndpoints с MapRazorPages) для добавления конечных точек страниц Razor в поток запросов.
UseExceptionHandler — это первый компонент промежуточного слоя, добавленный в конвейер. Таким образом, обработчик исключений перехватывает все исключения, возникающие в последующих вызовах.
Компонент промежуточного слоя для статических файлов вызывается на раннем этапе конвейера, чтобы он мог обрабатывать запросы и прекратить выполнение, минуя остальные компоненты. Этот компонент не выполняет проверки авторизации. Все файлы, предоставляемые Static File Middleware, включая те, что находятся в wwwroot, являются публичными. Сведения о защите статических файлов см. в статье Статические файлы в ASP.NET Core.
Если запрос не обрабатывается компонентом промежуточного слоя для статических файлов, он передается в компонент промежуточного слоя для проверки подлинности (UseAuthentication), который выполняет проверку подлинности. Аутентификация не останавливает неаутентифицированные запросы. Хотя промежуточное ПО для аутентификации проверяет подлинность запросов, авторизация (и отказ) выполняются только после того, как MVC выберет конкретную Razor страницу или контроллер MVC и действие.
Следующий пример демонстрирует порядок работы промежуточного слоя, где запросы на статические файлы обрабатываются промежуточным слоем для статических файлов до слоя для сжатия ответов. Статические файлы не сжимаются с таким порядком промежуточного ПО. Ответы на Razor страницах могут быть сжаты.
Дополнительные сведения об одностраничных приложениях см. в руководствах по шаблонам проектов React и Angular.
Порядок UseCors и UseStaticFiles
Порядок вызовов UseCors и UseStaticFiles зависит от приложения. Дополнительные сведения см. в разделе Порядок UseCors и UseStaticFiles.
Порядок промежуточного ПО обработки заголовков
Промежуточное ПО перенаправления заголовков должно выполняться перед другими промежуточными ПО. Такой порядок гарантирует, что промежуточное ПО, полагающееся на сведения о перенаправленных заголовках, может использовать значения заголовков для обработки. Сведения о запуске ПО промежуточного слоя перенаправления заголовков после ПО промежуточного слоя диагностики и обработки ошибок см. в разделе Порядок ПО промежуточного слоя перенаправления заголовков.
Разветвить конвейер программного обеспечения промежуточного слоя
Расширения Map применяются как стандарт для ветвления конвейера.
Map разделяет конвейер запросов на основе совпадений заданного пути запроса. Если путь запроса начинается с заданного пути, данная ветвь выполняется.
Map также может сопоставить несколько сегментов одновременно:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen осуществляет ветвление конвейера запросов на основе результата заданного предиката. Любой предикат типа Func<HttpContext, bool> можно использовать для сопоставления запросов с новой ветвью конвейера. В следующем примере предикат служит для определения наличия переменной строки запроса branch.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
Ниже приведены запросы и отклики http://localhost:1234 на базе предыдущего кода.
Запрос
Ответ
localhost:1234
Hello from non-Map delegate.
localhost:1234/?branch=main
Branch used = main
UseWhen также осуществляет ветвление конвейера запросов на основе результата заданного предиката. В отличие от MapWhen, эта ветвь снова объединяется с основным конвейером, если она не выполняется по сокращенной схеме или не содержит конечное ПО промежуточного слоя:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
В предыдущем примере ответ Hello from non-Map delegate. записывается для всех запросов. Если запрос включает переменную строки запроса branch, ее значение регистрируется до того, как будет выполнено повторное объединение с основным конвейером.
Встроенное промежуточное ПО
ASP.NET Core содержит следующие компоненты промежуточного слоя. В столбце Порядок указаны сведения о размещении ПО промежуточного слоя в конвейере обработки запросов и условия, в соответствии с которыми ПО промежуточного слоя может прервать обработку запроса. Если промежуточный слой замыкает конвейер обработки запроса и препятствует обработке запроса дальнейшими компонентами промежуточного слоя, он называется терминальным промежуточным слоем. Дополнительные сведения о коротком замыкании см. в разделе «Создание конвейера промежуточного слоя с помощью WebApplication».
Создает страницу со сведениями об ошибках, предназначенными для использования только в среде разработки.
Ставится перед компонентами, выдающими ошибки. Шаблоны проектов автоматически регистрируют это промежуточное ПО первым элементом в конвейере при использовании среды разработки.
Отдельное ПО промежуточного слоя, которое обеспечивает обработку исключений, предоставляет страницу исключений для разработчика, страницы состояния кода, веб-страницу по умолчанию для новых приложений.
Ставится перед компонентами, выдающими ошибки. Терминал для обработки исключений или показа страницы по умолчанию для новых приложений.
Перед компонентами, чувствительными к локализации. Должен отображаться после промежуточного ПО маршрутизации при использовании RouteDataRequestCultureProvider.
Обрабатывает все запросы с этой точки в цепочке промежуточного программного обеспечения, возвращая страницу по умолчанию для одностраничного приложения (SPA)
В конце цепочки, чтобы другое ПО промежуточного слоя для обслуживания статических файлов, действий MVC и т. д. имело приоритет.
Ставится перед компонентами, которые должны обрабатывать запросы WebSocket.
Дополнительные ресурсы
Параметры времени существования и регистрации — раздел содержит полный пример ПО промежуточного слоя со службами, имеющими время существования scoped (с заданной областью), transient (временное) и singleton (отдельное).
ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов. Каждый компонент:
определяет, нужно ли передать запрос следующему компоненту в конвейере;
может выполнять работу как до, так и после вызова следующего компонента в конвейере.
Для построения конвейера запросов используются делегаты запроса. Доверенные обработчики запросов обрабатывают каждый HTTP-запрос.
Делегаты запроса настраиваются с помощью методов расширения Run, Map и Use. Отдельный делегат запроса можно указать встроенным кодом как анонимный метод (называемый встроенным промежуточным ПО) либо определить в переиспользуемом классе. Эти многоразовые классы и встроенные анонимные методы являются посредничающим ПО или компонентами посредничающего ПО. Каждый компонент ПО промежуточного слоя в конвейере запросов отвечает за вызов следующего компонента в конвейере или замыкает конвейер. Когда промежуточный слой прерывает выполнение, он становится терминальным промежуточным слоем, так как препятствует обработке запроса дальнейшими промежуточными слоями.
Создание конвейера ПО промежуточного слоя с помощью IApplicationBuilder
Конвейер запросов ASP.NET Core состоит из последовательности делегатов запроса, вызываемых один за другим. На следующей схеме демонстрируется этот принцип. Поток выполнения показан черными стрелками.
Каждый из делегатов может выполнять операции до и после следующего делегата. Делегаты обработки исключений должны вызываться в начале конвейера, чтобы перехватывать исключения, возникающие на более поздних этапах.
Простейшее приложение ASP.NET Core задает один делегат запроса, обрабатывающий все запросы. В этом случае конвейер запросов как таковой отсутствует. Вместо этого в ответ на каждый HTTP-запрос вызывается одна анонимная функция.
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
}
}
Несколько делегатов запроса можно соединить в цепочку с помощью Use. Параметр next представляет следующий делегат в конвейере. Вы можете прервать поток, не вызывая параметр next. Обычно действия можно выполнять как до, так и после следующего делегата, как показано в этом примере.
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
}
}
Если делегат не передает запрос следующему делегату, это называется замыканием конвейера запросов. Короткое замыкание часто является предпочтительным, так как позволяет избежать ненужной работы. Например, промежуточное ПО для статических файлов может выступать в качестве завершающего промежуточного ПО, обрабатывая запрос к статическому файлу и прерывая остальную часть конвейера. Компоненты промежуточного слоя, добавленные перед промежуточным слоем, выполняющим завершение дальнейшей обработки, по-прежнему обрабатывают код после своих инструкций next.Invoke. Однако, обратите внимание на следующее предупреждение о попытке записи в ответ, который уже был отправлен.
может вызвать нарушение протокола, например, записывать больше, чем указано Content-Length;
может привести к нарушению формата тела. например, при записи нижнего колонтитула HTML в CSS-файл.
HasStarted удобно использовать для обозначения того, были ли отправлены заголовки или записано содержимое в тело сообщения.
Делегаты Run не получают параметр next. Первый делегат Run всегда является конечным и завершает конвейер.
Run — это соглашение. Некоторые компоненты промежуточного слоя могут предоставлять методы Run[Middleware], которые выполняются в конце конвейера:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
}
}
В предыдущем примере делегат Run записывает "Hello from 2nd delegate." в ответ и завершает конвейер. Если добавить другой делегат Use или Run после делегата Run, он не будет вызван.
Заказ промежуточного ПО
На следующей схеме показан полный конвейер обработки запросов для приложений ASP.NET Core MVC и Razor Pages. В стандартном приложении вы можете увидеть, как упорядочены существующие ПО промежуточного слоя и где добавляются пользовательские ПО промежуточного слоя. Вы можете полностью контролировать, как изменять порядок существующего посреднического ПО или внедрять новое кастомное посредническое ПО, как необходимо для ваших сценариев.
ПО промежуточного слоя конечной точки на предыдущей схеме выполняет конвейер фильтра для соответствующего типа приложения — MVC или Razor Pages.
Порядок, в котором компоненты промежуточного слоя добавляются в метод Startup.Configure, определяет порядок их вызова при запросах и обратный порядок для отклика. Соблюдать этот порядок крайне важно для обеспечения безопасности, производительности и функциональности.
Следующий метод Startup.Configure добавляет связанные с безопасностью компоненты ПО промежуточного слоя в стандартном рекомендуемом порядке:
Не каждое ПО промежуточного слоя следует именно этому порядку, но многие — да. Например:
UseCors, UseAuthentication и UseAuthorization должны присутствовать в указанном порядке.
Сейчас UseCors нужно использовать перед UseResponseCaching из-за этой ошибки.
UseRequestLocalization должен находиться перед любым промежуточным ПО, которое может проверять культурные настройки запроса (например, app.UseMvcWithDefaultRoute()).
В некоторых сценариях ПО промежуточного слоя имеет другой порядок. Например, порядок кэширования и сжатия зависит от сценария, и существует несколько допустимых вариантов такого порядка. Например:
С помощью приведенного выше кода можно экономить ресурсы ЦП путем кэширования сжатого ответа, но при этом может быть выполнено кэширование нескольких представлений ресурса с помощью разных алгоритмов сжатия, таких как Gzip или Brotli.
В приведенном ниже порядке объединяются статические файлы, чтобы разрешить кэширование сжатых статических файлов.
Метод Startup.Configure добавляет компоненты ПО промежуточного слоя для распространенных сценариев приложений:
Обработка исключений и ошибок
При запуске приложения в среде разработки:
Промежуточное программное обеспечение страницы исключений для разработчика (UseDeveloperExceptionPage) сообщает об ошибках во время выполнения приложения.
ПО промежуточного слоя страниц ошибок базы данных отчитывает об ошибках среды выполнения базы данных.
При запуске приложения в рабочей среде:
Промежуточное ПО для обработки исключений (UseExceptionHandler) перехватывает исключения, выбрасываемые в последующих промежуточных слоях.
ПО промежуточного слоя протокола HTTP Strict Transport Security Protocol (HSTS) (UseHsts) добавляет заголовок Strict-Transport-Security.
Промежуточное программное обеспечение перенаправления HTTPS (UseHttpsRedirection) перенаправляет запросы, отправленные по протоколу HTTP, на HTTPS.
Промежуточное ПО для статических файлов (UseStaticFiles) возвращает статические файлы и прекращает дальнейшую обработку запросов.
Политический промежуточный слой Cookie (UseCookiePolicy) обеспечивает соответствие приложения нормативам Общего регламента по защите данных (GDPR) ЕС.
Промежуточное ПО маршрутизации (UseRouting) для маршрутизации запросов.
ПО промежуточного слоя проверки подлинности (UseAuthentication) пытается проверить подлинность пользователя, прежде чем предоставить ему доступ к защищенным ресурсам.
ПО промежуточного слоя авторизации (UseAuthorization) разрешает пользователю доступ к защищенным ресурсам.
Промежуточное ПО для сеанса (UseSession) создает и поддерживает состояние сеанса. Если в приложении используется состояние сеанса, вызовите ПО промежуточного слоя сеанса после ПО промежуточного слоя политики Cookie и до ПО промежуточного слоя MVC.
Промежуточное программное обеспечение маршрутизации конечных точек (UseEndpoints с MapRazorPages) для добавления конечных точек Razor Pages в конвейер обработки запросов.
UseExceptionHandler — это первый компонент промежуточного слоя, добавленный в конвейер. Таким образом, промежуточное программное обеспечение для обработки исключений перехватывает любые исключения, возникающие в более поздних вызовах.
Компонент промежуточного слоя для статических файлов вызывается на раннем этапе конвейера, чтобы он мог обрабатывать запросы и быстро завершать их, минуя остальные компоненты. Промежуточное ПО для статических файлов не выполняет проверки авторизации. Все файлы, передаваемые Static File Middleware, включая расположенные в wwwroot, имеют публичный доступ. Сведения о защите статических файлов см. в статье Статические файлы в ASP.NET Core.
Если запрос не обрабатывается компонентом промежуточного слоя для статических файлов, он передается в компонент промежуточного слоя для проверки подлинности (UseAuthentication), который выполняет проверку подлинности. Аутентификация не прекращает выполнение запросов, не прошедших проверку. Хотя Middleware для аутентификации проверяет запросы, авторизация (и отклонение) выполняется только после того, как MVC выберет определенную страницу Razor или контроллер MVC и действие.
Следующий пример демонстрирует порядок компонентов промежуточного слоя, где запросы на статические файлы обрабатываются компонентом обработки статических файлов перед компонентом сжатия откликов. Статические файлы не сжимаются при данном порядке промежуточного ПО. Ответы Razor Pages могут быть сжаты.
Для одностраничных приложений (SPA) ПО промежуточного слоя SPA UseSpaStaticFiles обычно поступает в конвейер ПО промежуточного слоя последним. ПО промежуточного слоя SPA поступает последним:
Чтобы разрешить остальным ПО промежуточного слоя отвечать на запросы сопоставления в первую очередь.
Чтобы разрешить выполнение одностраничных приложений с маршрутизацией на стороне клиента для всех маршрутов, которые не распознаются серверным приложением.
Дополнительные сведения об SPA см. в руководствах по шаблонам проектов React и Angular.
Порядок промежуточного ПО для перенаправления заголовков
Промежуточное ПО для перенаправления заголовков должно выполняться перед другим промежуточным ПО. Такой порядок гарантирует, что промежуточное программное обеспечение, которое опирается на информацию о перенаправленных заголовках, может использовать значения этих заголовков для обработки. Сведения о запуске ПО промежуточного слоя перенаправления заголовков после ПО промежуточного слоя диагностики и обработки ошибок см. в разделе Порядок ПО промежуточного слоя перенаправления заголовков.
Ветвление конвейера промежуточного ПО
Расширения Map используются как стандартный способ ветвления конвейера.
Map осуществляет ветвление конвейера запросов на основе совпадений для заданного пути запроса. Если путь запроса начинается с заданного пути, данная ветвь выполняется.
public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
private static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
Ниже приведены запросы и отклики http://localhost:1234 на базе предыдущего кода.
Запрос
Ответ
localhost:1234
Привет от делегата non-Map.
localhost:1234/map1
Тест карты 1
localhost:1234/map2
Тест карты 2
localhost:1234/map3
Привет от делегата, не связанного с Map.
Когда используется Map, соответствующие сегменты путей удаляются из HttpRequest.Path и добавляются к HttpRequest.PathBase для каждого запроса.
Map также может сопоставить несколько сегментов одновременно:
public class Startup
{
private static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map multiple segments.");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
MapWhen осуществляет ветвление конвейера запросов на основе результата заданного предиката. Любой предикат типа Func<HttpContext, bool> можно использовать для сопоставления запросов с новой ветвью конвейера. В следующем примере предикат служит для определения наличия переменной строки запроса branch.
public class Startup
{
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
public void Configure(IApplicationBuilder app)
{
app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
Ниже приведены запросы и отклики http://localhost:1234 на базе предыдущего кода.
Запрос
Ответ
localhost:1234
Привет от делегата не-для-карты.
localhost:1234/?branch=main
Ветка используется = main
UseWhen также осуществляет ветвление конвейера запросов на основе результата заданного предиката. В отличие от MapWhen, эта ветвь снова объединяется с основным конвейером, если она не имеет короткого замыкания или не содержит терминальное промежуточное программное обеспечение:
public class Startup
{
private void HandleBranchAndRejoin(IApplicationBuilder app, ILogger<Startup> logger)
{
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder, logger));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from main pipeline.");
});
}
}
В предыдущем примере для всех запросов записывается ответ "Привет от главного канала.". Если запрос включает переменную строки запроса branch, ее значение регистрируется до того, как будет выполнено повторное объединение с основным конвейером.
встроенное промежуточное ПО
ASP.NET Core содержит следующие компоненты промежуточного слоя. В столбце Порядок указаны сведения о размещении ПО промежуточного слоя в конвейере обработки запросов и условия, в соответствии с которыми ПО промежуточного слоя может прервать обработку запроса. Если промежуточный слой замыкает конвейер обработки запроса и препятствует обработке запроса дальнейшими компонентами промежуточного слоя, он называется терминальным промежуточным слоем. Дополнительные сведения о коротком замыкании см. в разделе Создание конвейера ПО промежуточного слоя с помощью IApplicationBuilder.
Отдельное ПО промежуточного слоя, которое обеспечивает обработку исключений, предоставляет страницу исключений для разработчика, страницы состояния кода, веб-страницу по умолчанию для новых приложений.
Ставится перед компонентами, выдающими ошибки. Предназначен для обработки исключений или отображения веб-страницы по умолчанию для новых приложений.
Перед компонентами, чувствительными к локализации. Должен появляться после маршрутизирующего промежуточного ПО при использовании RouteDataRequestCultureProvider.
Перед компонентами, которые должны принимать запросы WebSocket.
Дополнительные ресурсы
Параметры времени существования и регистрации содержат полный пример промежуточного программного обеспечения со службами, имеющими время существования scoped, transient, и singleton.
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Отзыв о
ASP.NET Core
ASP.NET Core
— это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв:
Изучите и реализуйте ПО промежуточного слоя в приложении ASP.NET Core. Используйте включаемые ПО промежуточного слоя, такие как ведение журнала HTTP и проверка подлинности. Создание пользовательского ПО промежуточного слоя для обработки запросов и ответов.