Конвейер обработки запроса

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 01.11.2019

Как правило, для обработки запроса применяется не один, а несколько компонентов middleware. И в этом случае большую роль может играть порядок их помещения в конвейер обработки запроса, а также то, как они взаимодействуют с другими компонентами.

Кроме того, каждый компонент middleware может обрабатывать запрос до и после последующих в конвейере компонентов. Данное обстоятельство позволяет предыдущим компонентам корректировать результат обработки последующих компонентов.

Рассмотрим простейший пример. Определим следующий middleware для обработки запроса:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace HelloApp
{
    public class RoutingMiddleware
    {
        private readonly RequestDelegate _next;
        public RoutingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            string path = context.Request.Path.Value.ToLower();
            if (path == "/index")
            {
                await context.Response.WriteAsync("Home Page");
            }
            else if (path == "/about")
            {
                await context.Response.WriteAsync("About");
            }
            else
            {
                context.Response.StatusCode = 404;
            }
			//await _next.Invoke(context);
        }
    }
}

Этот компонент в зависимости от строки запроса возвращает либо определенную строку, либо устанавливает код ошибки.

Допустим, мы хотим, чтобы пользователь был аутентифицирован при обращении к нашему приложению. Для этого добавим новый класс AuthenticationMiddleware:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace HelloApp
{
    public class AuthenticationMiddleware
    {
        private RequestDelegate _next;
        public AuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            var token = context.Request.Query["token"];
            if (string.IsNullOrWhiteSpace(token))
            {
                context.Response.StatusCode = 403;
            }
            else
            {
                await _next.Invoke(context);
            }
        }
    }
}

Условно будем считать, что если в строке запроса есть параметр token и он имеет какое-нибудь значение, то пользователь аутентифицирован. А если он не аутентифицирован, то надо необходимо ограничить доступ пользователям к приложению. Если пользователь не аутентифицирован, то устанавливаем статусный код 403, иначе передаем выполнение запроса следующему в конвейере делегату.

Поскольку компоненту RoutingMiddleware нет смысла обрабатывать запрос, если пользователь не аутентифицирован, то в конвейере компонент AuthenticationMiddleware должен быть помещен перед компонентом RoutingMiddleware:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<AuthenticationMiddleware>();
        app.UseMiddleware<RoutingMiddleware>();
    }
}

Таким образом, если мы сейчас запустим проект и обратимся по пути /index или /about и не передадим параметр token, то мы получим ошибку. Если же обратимся по пути /index или /about и передадим значение параметра token, то увидим искомый текст:

Организация pipeline в asp net core

Добавим еще один компонент middleware, который назовем ErrorHandlingMiddleware:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace HelloApp
{
    public class ErrorHandlingMiddleware
    {
        private RequestDelegate _next;
        public ErrorHandlingMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            await _next.Invoke(context);
            if (context.Response.StatusCode == 403)
            {
                await context.Response.WriteAsync("Access Denied");
            }
            else if (context.Response.StatusCode == 404)
            {
                await context.Response.WriteAsync("Not Found");
            }
        }
    }
}

В отличие от предыдущих двух компонентов ErrorHandlingMiddleware сначала передает запрос на выполнение последующим делегатам, а потом уже сам обрабатывает. Это возможно, поскольку каждый компонент обрабатывает запрос два раза: вначале вызывается та часть кода, которая идет до await _next.Invoke(context);, а после завершения обработки последующих компонентов вызывается та часть кода, которая идет после await _next.Invoke(context);. И в данном случае для ErrorHandlingMiddleware важен результат обработки запроса последующими компонентами. В частности, он устанавливает сообщения об ошибках в зависимости от того, как статусный код установили другие компоненты. Поэтому ErrorHandlingMiddleware должен быть помещен первым из всех трех компонентов:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<ErrorHandlingMiddleware>();
        app.UseMiddleware<AuthenticationMiddleware>();
        app.UseMiddleware<RoutingMiddleware>();
    }
}

Схематично конвейер обработки запроса будет выглядеть следующим образом:

Конвейер обработки запроса в ASP.NET Core

В то же время, если к приложению обратится пользователь, не указав в строке запроса параметр token, то AuthenticationMiddleware не будет передавать дальше запрос на обработку, а конвейер обработки будет выглядеть так:

Middleware pipeline in ASP.NET Core

В первом случае, если указан параметр token, то запрос будет обработан RoutingMiddleware:

Этапы обработки запроса в ASP.NET Core

Иначе пользователь получит ошибку 403:

Цикл обработки запроса в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850