RouterMiddleware

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

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

В прошлой теме были рассмотрены стандартный механизм маршрутизации в ASP.NET Core с помощью компонентов EndpointRoutingMiddleware и EndpointMiddleware. Еще один способ построения маршрутизации представляет компонент называется RouterMiddleware. При обработке запроса RouterMiddleware сравнивает запрашиваемый адрес Url с зарегистрированными маршрутами, и если один из маршрутов подходит, то вызывается обработчик этого маршрута.

Для добавления RouterMiddleware изменим класс Startup:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Routing;

namespace RoutingApp
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app)
        {
            // определяем обработчик маршрута
            var myRouteHandler = new RouteHandler(Handle);
			// создаем маршрут, используя обработчик
            var routeBuilder = new RouteBuilder(app, myRouteHandler);
			// само определение маршрута - он должен соответствовать запросу {controller}/{action}
            routeBuilder.MapRoute("default", "{controller}/{action}");
			// строим маршрут
			app.UseRouter(routeBuilder.Build());
			
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        }
		
		// собственно обработчик маршрута
        private async Task Handle(HttpContext context)
        {
            await context.Response.WriteAsync("Hello ASP.NET Core!");
        }
    }
}

В методе Configure() происходит настройка маршрута и определение обработчика, который будет обрабатывать этот запрос по этому маршруту:

var myRouteHandler = new RouteHandler(Handle);
var routeBuilder = new RouteBuilder(app, myRouteHandler);
routeBuilder.MapRoute("default", "{controller}/{action}");
app.UseRouter(routeBuilder.Build());

Класс RouteHandler представляет встроенный обработчик маршрута. В качестве параметра в него передается делегат RequestDelegate, который и будет обрабатывать запрос. В данном случае на место делегата передается ссылка на метод Handle(). Метод, который представляет делегат RequestDelegate, должен в качестве параметра принимать контекст запроса HttpContext и возвращать объект Task. В данном случае этот метод просто направляет в выходной поток строку "Hello ASP.NET Core!"

Для построения маршрута применяется объект RouteBuilder. В его конструктор передается сервис IApplicationBuilder, который в данном случае мы можем получить из параметра метода Configure(), и вышеопределенный обработчик маршрута.

Далее идет собственно определение самого маршрута в методе routeBuilder.MapRoute(). В данном случае "default" задает имя маршрута, а строка "{controller}/{action}" представляет шаблон маршрута - некоторый шаблон Url, с которым будет сопоставляться запрошенный адрес URL. Шаблон URL может состоять из одного и более сегментов. Если в шаблоне используется несколько сегментов, то они разделяются слешами.

Каждый такой сегмент шаблона содержит параметр. Эти параметры называются параметрами маршрута. Каждый параметр заключается в фигурные скобки. В данном случае это параметры controller и action. Но вообще параметры не обязательно должны иметь именно такие названия, они могут иметь различные имена, включающие любые алфавитно-цифровые символы.

И последняя строка подключает RouterMiddleware в конвейер обработки запроса:

app.UseRouter(routeBuilder.Build());

Вызов routeBuilder.Build() возвращает объект IRouter, который затем переходит в RouterMiddleware и используется для обработки запросов.

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

При этом после этого вызова могут идти другие компоненты middleware, которые могут участвовать в обработке запроса. Так, в данном случае в конце вызывается метод app.Run(), который отправляет в выходной поток строку "Hello World".

Теперь запустим приложение на выполнение. По умолчанию приложение открывается по адресу типа http://localhost:xxxx/ - то есть название домена и порта.

Маршрутизация в ASP.NET Core

При этом надо учитывать, что RouterMiddleware не передает выполнение запроса дальше, если хотя бы один из определенных маршрутов совпал со строкой запроса.

URL matching

Когда к приложению, которое использует маршруты, приходит запрос, то начинается процесс URL matching - процесс сопоставления строки запроса URL маршрутам. Причем в приложении может использоваться множество маршрутов. И RouterMiddleware последовательно перебирает все маршруты и сравнивает их шаблон с запрошенным адресом URL. Если шаблон маршрута и строка URL совпадают, то для обработки запроса выбирается обработчик данного маршрута. Если обработчик установлен, то он вызывается, и далее другие маршруты не вызываются. Если шаблон маршрута и запрошенный адрес URL не совпадают, тогда вызывается следующий маршрут.

Если все маршруты были вызваны, но обработчик для запроса так и не был найден, RouterMiddleware вызывает следующий в конвейере обработки запроса компонент middleware.

Так, в данном случае запрошенный URL выглядит следующим образом: http://localhost:59406. Этот URL не имеет сегментов после домена и порта (домен и порт в сегментной структуре не учитываются). А наш единственный маршрут задает шаблон из двух сегментов: {controller}/{action}. То есть в процессе URL matching компонент RouterMiddleware не найдет нужный маршрут и просто передаст выполнение запроса дальше методу app.Run(), который и выведет строку "Hello World!".

Теперь обратимся к нашему приложению с другим запросом http://localhost:59406/home/index:

Основы маршрутизации в ASP.NET Core

В этом случае строка URL уже имеет два сегмента: home/index. Теперь система маршрутизации сможет сопоставить адрес запроса с единственным маршрутом, так как адрес запрос также состоит из двух сегментов. Для инкапсулирования всей информации о маршрутизации в RouterMiddleware создается объект RouteContext

Для полного понимания процесса мы можем обратиться к исходным кодам классов RouteHandler и RouterMiddleware. Так, при удачном сопоставлении запроса определенному маршруту во встроенном классе RouteHandler у контекста маршрутизации устанавливается свойство Handler. И этому свойству присваивается тот делегат RequestDelegate, который мы передаем через объект RouteHandler:

public class RouteHandler : IRouteHandler, IRouter
{
	private readonly RequestDelegate _requestDelegate;

    public RouteHandler(RequestDelegate requestDelegate)
    {
        _requestDelegate = requestDelegate;
    }
	
	// остальное содержимое класса

    public Task RouteAsync(RouteContext context)
    {
        context.Handler = _requestDelegate;
        return TaskCache.CompletedTask;
	}
}

И затем в методе Invoke класса RouterMiddleware вызывается этот делегат:

public class RouterMiddleware
{
    // остальное содержимое
    public async Task Invoke(HttpContext httpContext)
    {
        var context = new RouteContext(httpContext);
        context.RouteData.Routers.Add(_router);
			
		// проверка соответствия строки запроса маршруту
        await _router.RouteAsync(context);

		// если сопоставление завершилось неудачно
		// и делегат не установлен, 
        if (context.Handler == null)
        {
            _logger.RequestDidNotMatchRoutes();
			// вызываем следующий компонент middleware
            await _next.Invoke(httpContext);
        }
        else
        {
            var routingFeature = new RoutingFeature()
			{
				RouteData = context.RouteData
			};

			httpContext.Request.RouteValues = context.RouteData.Values;
			httpContext.Features.Set<IRoutingFeature>(routingFeature);

			await context.Handler(context.HttpContext);
        }
    }
	// остальное содержимое класса
}

Поэтому при удачном сопоставлении с запросом система маршрутизации вызовет делегат RequestDelegate, который в данном случае указывает на метод Handle() и который отправит в ответ на запрос строку "Hello ASP.NET Core".

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850