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:
2909

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
Jul 19, 2021
Author: Jignesh Trivedi
As we aware that framework .net core 2.1 is now under LTS (Long Term Support) releases. So, this framework is more stable and may use to create a large application. When we talk about web application, security is a major...
Aug 8
Author: Davit Asryan
The growth of the internet has made instant communication technology more important than ever, especially for the Internet of Things (IoT). With so many devices like smart home gadgets and industrial sensors needing to talk to each other smoothly, having...
Jul 21
Author: Henrik Arent
Choosing between .NET Core and .NET Framework is a crucial decision for your business. Your choice between them can significantly impact the smoothness of your software projects and the success of your business. Both of these platforms have their own...
Jul 16, 2020
Author: Kannan Eswar
In this blog, I am going to provide a walkthrough on developing REST APIs using ASP.NET Core 3.1, connecting with existing databases using Entity Framework, creating a JWT token, and securing APIs. I am going to develop a sample application...
Send message
Type
Email
Your name
*Message