LinkedIn
Проекты
Advertisement
RSS
разработка сайтов
оптимизация сайта
веб-дизайн
продвижение сайтов
ASP.NET
ASP.NET MVC
.NET Core
HTML5
SEO
CSS3
jQuery
Bootstrap
Angular
React
Always will be ready notify the world about expectations as easy as possible: job change page

Разбираемся с middleware в ASP.NET Core

Добавлено: 28 июня 2021 г.
Автор: MaxRokatansky
Источник: https://habr.com/ru/company/otus/blog/528692/
Просмотров: 122

ASP.NET Core Web App Middleware Pipeline

Эта статья раскрывает концепции 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 для дальнейшей обработки.
  • При необходимости может завершить (замкнуть) конвейер запросов.
  • Выполняется в том порядке, в котором он был добавлены в конвейер.

Надеюсь, вы что-нибудь для себя почерпнули из этой статьи! Удачи вам в обучении! 

Похожее
26 Jul 2019
Author: Marinko Spasojevic
While we are working on a project, our main goal is to make it work as it supposed to and fulfill all the customer’s requirements.But wouldn’t you agree that creating a project that works is not enough? Shouldn’t that project...
15 Jun 2021
Author: Emre Kizildas
Hi everyone, this article include encrypting your database columns with EntityFramework. Your database providers can be MSSQL or PostgreSQL.Considering that some database columns contain confidential data, it is of great importance that the data is encrypted in case of capture...
24 Aug 2022
Author: Jaydeep Patil
In this article, we will learn about global exception handling implementation using .NET Core 6 Web API step-by-step.Agenda Introduction Implementation of Global Exception HandlingPrerequisites Visual Studio 2022 .NET...
17 Nov 2020
RSS stands for "Really Simple Syndication". It is a web feed mechanism, which helps applications or clients get updated content from websites in a timely fashion and empower applications to get content from more than one website regularly without having...
Написать сообщение
Эл. почта
Имя
*Сообщение


© 1999–2022 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET | .NET Core | C# | ASP.NET | Windows Forms | WPF | Windows Phone | HTML5 | CSS3 | jQuery | AJAX | MS SQL Server | Transact-SQL | ADO.NET | Entity Framework | IIS | OOP | OOA | OOD | WCF | WPF | MSMQ | MVC | MVP | MVVM | Design Patterns | Enterprise Architecture | Scrum | Kanban