Advertisement
Search  
Always will be ready notify the world about expectations as easy as possible: job change page
28 июня 2021 г.

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

Author:
MaxRokatansky
Source:
Views:
1117

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

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

Similar
Apr 12, 2023
Author: Jaimin Soni
HTTP/3 is the latest version of the HTTP protocol, built on top of the QUIC transport protocol. It offers several advantages over its predecessors, including faster and more reliable connections, improved security, and reduced latency. In this article, we will...
Aug 15, 2023
Whether you have an app with just a few users or millions of users per day, like Agoda, improving the user experience by optimizing application performance is always crucial.In the case of very high-traffic websites in the cloud, this optimization...
Dec 25, 2023
Author: Binod Mahto
Testing coverage is the key part of any project development and Testing coverage is not only about the unit testing instead Integration Testing is extremely important to make sure entire system is unbroken & bug free with any change or...
Feb 15
Author: Pranaya Rout
Kestrel Web Server in ASP.NET Core ApplicationIn this article, I will discuss the Kestrel Web Server in ASP.NET Core application with examples. Please read our previous article discussing the ASP.NET Core InProcess Hosting Model with examples. At the end of...
Send message
Email
Your name
*Message


© 1999–2024 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET Framework | .NET Core | C# | ASP.NET | Windows Forms | WPF | HTML5 | CSS3 | jQuery | AJAX | Angular | React | 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