В преддверии курса «C# ASP.NET Core разработчик» приглашаем вас записаться на открытый урок по теме «Логирование и трейсинг запросов в asp.net core».
А пока делимся с вами традиционным полезным переводом.
Этой статья раскрывает концепции Middleware в ASP.NET Core. К концу этой статьи вы получите четкое представление о следующих моментах:
-
Что такое Middleware?
-
Почему порядок расположения Middleware имеет значение?
-
Методы Run, Use и Map.
-
Как создать собственное Middleware?
-
Как реализовать просмотр каталогов с помощью Middleware?
Что такое Middleware?
Middleware (промежуточное или связующее программное обеспечение) — это фрагмент кода в конвейере приложения, используемый для обработки запросов и ответов.
Например, у нас может быть middleware-компонент для аутентификации пользователя, middleware-компонент для обработки ошибок и еще один middleware-компонент для обслуживания статических файлов, таких как файлы JavaScript, CSS, разного рода изображения и т. д.
Middleware может быть встроенным как часть платформы .NET Core, добавляемым через пакеты NuGet или же написанным самим пользователем. Middleware-компоненты настраиваются в методе Сonfigure класса запуска приложения (Startup). Метод Configure выстраивает конвейер обработки запросов в ASP.NET Core приложении. Он состоит из последовательности делегатов запросов, вызываемых один за другим.
На рисунке ниже показано, как запрос обрабатывается middleware-компонентами.

Как правило, каждое middleware обрабатывает входящие запросы и передает выполнение следующему middleware для дальнейшей обработки.
Но middleware-компонент также может решить не вызывать следующую часть middleware в конвейере. Это называется замыканием (short-circuiting) или завершением конвейера запросов. Замыкание зачастую желательно, поскольку оно позволяет избежать ненужной работы. Например, если это запрос статического файла, такого как файл CSS, JavaScript, изображение и т. д., middleware-компонент для статических файлов может обработать и обслужить этот запрос, а затем замкнуть остальную часть конвейера.
Давайте создадим ASP.NET Core веб-приложение и рассмотрим конфигурацию middleware по умолчанию в методе Configure класса Startup.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { //This middleware is used reports app runtime errors in development environment. app.UseDeveloperExceptionPage(); } else { //This middleware is catches exceptions thrown in production environment. 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(); //adds the Strict-Transport-Security header. } //This middleware is used to redirects HTTP requests to HTTPS. app.UseHttpsRedirection(); //This middleware is used to returns static files and short-circuits further request processing. app.UseStaticFiles(); //This middleware is used to route requests. app.UseRouting(); //This middleware is used to authorizes a user to access secure resources. app.UseAuthorization(); //This middleware is used to add Razor Pages endpoints to the request pipeline. app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); }
Фрейсворк ASP.NET Core предоставляет встроенные middleware-компоненты, которые мы можем легко использовать, добавляя в метод Configure. Ознакомьтесь с документацией Microsoft для получения более подробной информации.
Упорядочение Middleware
Middleware-компоненты выполняются в том порядке, в котором они добавляются в конвейер, по этому следует проявлять осторожность и добавлять middleware в правильном порядке, иначе приложение может работать не так, как вы ожидаете. Порядок расположения middleware важен для безопасности, производительности и функциональности.
Следующие middleware-компоненты предназначены для стандартных сценариев приложений и расположены в рекомендуемом порядке:

Первый middleware-компонент в конфигурации получил запрос, изменил его (при необходимости) и передал управление следующему middleware. Точно так же первый middleware-компонент выполняется последним при обработке ответа, если мы возвращаем обратно эхо. Вот почему делегаты обработки исключений должны вызываться на самых ранних этапах конвейера — чтобы они могли проверить результат и отобразить возможное исключение в удобном для браузера и клиента виде.
Методы Run, Use и Map
app.Run()
Этот метод добавляет middleware-компонент в виде Run[Middleware], который выполнится в конце конвейера. Как правило, он действует как замыкающее middleware и добавляется в конце конвейера запросов, поскольку не может вызывать следующий middleware-компонент.
app.Use()
Этот метод используется для конфигурирования нескольких middleware. В отличие от app.Run(), мы можем включить в него параметр next, который вызывает следующий делегат запроса в конвейере. Мы также можем замкнуть (завершить) конвейер, не вызывая параметр next.
Давайте рассмотрим следующий пример с app.Use() и app.Run() и проанализируем результат/ответ:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use(async (context, next) => { await context.Response.WriteAsync("Before Invoke from 1st app.Use()\n"); await next(); await context.Response.WriteAsync("After Invoke from 1st app.Use()\n"); }); app.Use(async (context, next) => { await context.Response.WriteAsync("Before Invoke from 2nd app.Use()\n"); await next(); await context.Response.WriteAsync("After Invoke from 2nd app.Use()\n"); }); app.Run(async (context) => { await context.Response.WriteAsync("Hello from 1st app.Run()\n"); }); // the following will never be executed app.Run(async (context) => { await context.Response.WriteAsync("Hello from 2nd app.Run()\n"); }); }

Первый делегат app.Run() завершает конвейер. В этом примере будет запущен только первый делегат («Hello from 1st app.Run()»), а запрос никогда не достигнет второго метода Run.
app.Map()
Этот метод расширения используются как условное обозначение для ветвления конвейера. Map разветвляет конвейер запросов на основе пути запроса. Если путь запроса начинается с указанного пути, ветвь выполняется.
Давайте рассмотрим следующий пример с app.Map() и проанализируем результат/ответ:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Map("/m1", HandleMapOne); app.Map("/m2", appMap => { appMap.Run(async context => { await context.Response.WriteAsync("Hello from 2nd app.Map()"); }); }); app.Run(async (context) => { await context.Response.WriteAsync("Hello from app.Run()"); }); } private static void HandleMapOne(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello from 1st app.Map()"); }); }
В следующей таблице показаны запросы и ответы от localhost с использованием приведенного выше кода.
|
Request |
Response |
|
https://localhost:44362/ |
Hello from app.Run() |
|
https://localhost:44362/m1 |
Hello from 1st app.Map() |
|
https://localhost:44362/m1/xyz |
Hello from 1st app.Map() |
|
https://localhost:44362/m2 |
Hello from 2nd app.Map() |
|
https://localhost:44362/m500 |
Hello from app.Run() |
Создание собственного Middleware
Middleware обычно инкапсулируется в класс и предоставляется с помощью метода расширения. Middleware может быть создано с помощью класса с методом InvokeAsync() и параметром типа RequestDelegate в конструкторе. Тип RequestDelegate требуется для выполнения следующего middleware в последовательности.
Рассмотрим пример, в котором нам нужно создать собственное middleware для регистрации URL-адреса запроса в веб-приложении.
public class LogURLMiddleware { private readonly RequestDelegate _next; private readonly ILogger<LogURLMiddleware> _logger; public LogURLMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) { _next = next; _logger = loggerFactory?.CreateLogger<LogURLMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory)); } public async Task InvokeAsync(HttpContext context) { _logger.LogInformation($"Request URL: {Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(context.Request)}"); await this._next(context); } }
public static class LogURLMiddlewareExtensions { public static IApplicationBuilder UseLogUrl(this IApplicationBuilder app) { return app.UseMiddleware<LogURLMiddleware>(); } }
В методе Configure:
app.UseLogUrl();
Реализация просмотра каталогов с помощью Middleware
Просмотр каталогов позволяет пользователям вашего веб-приложения видеть собственно сам список каталогов и файлы.
Просмотр каталогов по умолчанию отключен из соображений безопасности.
Давайте рассмотрим пример, в котором мы хотим реализовать просмотр списка изображений в браузере из папки с изображениями в wwwroot. Middleware UseDirectoryBrowser может обрабатывать и обслуживать эти изображения для такого рода запросов, а затем замкнуть остальную часть конвейера.
app.UseDirectoryBrowser(new DirectoryBrowserOptions { FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")), RequestPath = "/images" });

Резюме
Middleware в ASP.NET Core контролирует, как наше приложение отвечает на HTTP-запросы.
Таким образом, каждый middleware-компонент в ASP.NET Core:
-
Имеет доступ как к входящим запросам, так и к отправляемым обратно ответам.
-
Может просто передать запрос следующему middleware в конвейере.
-
Может выполнять некоторую логику обработки и затем передавать этот запрос следующему middleware для дальнейшей обработки.
-
При необходимости может завершить (замкнуть) конвейер запросов.
-
Выполняется в том порядке, в котором он был добавлены в конвейер.
Надеюсь, вы что-нибудь для себя почерпнули из этой статьи! Удачи вам в обучении!
Узнать подробнее о курсе и записаться на открытый урок можно здесь.
ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/528692/
Добавить комментарий