Маршрутизация

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

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

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

За сопоставление запросов с конкретными адресами внутри приложения в ASP.NET Core отвечает система маршрутизации. Для работы с системой маршрутизации в ASP.NET Core создадим простой проект по типу Empty, который назовем RoutingApp.

Если мы обратимся к коду класса Startup, который создается по умолчанию, то мы можем увидеть там примитивную систему маршрутизации:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace RoutingApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }
		
		public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            });
        }
    }
}

В частности, вызов

app.UseRouting();

Добавляет в конвейер обработки запроса компонент EndpointRoutingMiddleware. Система маршрутизации использует конечные точки (endpoints) для обработки запросов по определенным маршрутам. И компонент EndpointRoutingMiddleware как раз позволяет определить маршрут, который соответствует запрошенному адресу, и установить для его обработки конечную точку в виде объекта Microsoft.AspNetCore.Http.Endpoint, а также определить данные маршрута.

После того, как установлена конечная точка Endpoint, выполняется следующий вызов:

app.UseEndpoints(endpoints =>
{
	endpoints.MapGet("/", async context =>
	{
		await context.Response.WriteAsync("Hello World!");
	});
});

Метод app.UseEndpoints() встраивает в конвейер обработки компонент EndpointMiddleware. Он принимает делегат с одним параметром типа Microsoft.AspNetCore.Routing.IEndpointRouteBuilder, у которого можно вызвать ряд методов для установки обработчика определенных маршрутов. В частности, метод MapGet() добавляет конечную точку для определеного маршрута по запросу типа GET и ее обработчик.

Например, в данном случае определена только одна конечная точка, которая сопоставляется с маршрутом "/" (обращение к корню веб-приложения) и в ответ на запрос отправляет строку "Hello World".

Таким образом, при обращении к корню веб-приложения будет создана конечная точка для обработки маршрута "/", и в бразуере мы увидим строку "Hello World":

Endpoint Routing Middleware in ASP.NET Core

При этом при обращении по всем других адреса, которые не соответствуют обрабатываемым маршрутам, например, по адресу "/index", мы увидим, что ресурс не найден. Так как для них не установлено конечных точек.

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

Для более детального ознакомления с внутренней логикой системы маршрутизаци можно обратиться к исходным кодам, которые можно найти на github в каталоге https://github.com/aspnet/AspNetCore/tree/master/src/Http/Routing/src.

Чтобы чуть более детально разобраться в маршрутизации, изменим класс Startup следующим образом:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;

namespace RoutingApp
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();
            app.Use(async (context, next) =>
            {
                // получаем конечную точку
                Endpoint endpoint = context.GetEndpoint();

                if (endpoint != null)
                {
                    // получаем шаблон маршрута, который ассоциирован с конечной точкой
                    var routePattern = (endpoint as Microsoft.AspNetCore.Routing.RouteEndpoint)?.RoutePattern?.RawText;

                    Debug.WriteLine($"Endpoint Name: {endpoint.DisplayName}");
                    Debug.WriteLine($"Route Pattern: {routePattern}");

                    // если конечная точка определена, передаем обработку дальше
                    await next();
                }
                else
                {
                    Debug.WriteLine("Endpoint: null");
                    // если конечная точка не определена, завершаем обработку
                    await context.Response.WriteAsync("Endpoint is not defined");
                }
            });
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/index", async context =>
                {
                    await context.Response.WriteAsync("Hello Index!");
                });
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            });
        }
    }
}

Теперь между вызовами методов app.UseRouting() и app.UseEndpoints() мы добавили собственный middleware, чтобы отследить установленные данные. В этом middleware с помощью метода HttpContext.GetEndpoint() можно получить конечную точку для обработки текущего запроса, которая была установлена на предыдущем этапе EndpointRoutingMiddleware.

Endpoint endpoint = context.GetEndpoint();

Обратите внимание, что возможна ситуация, когда для обработки текущего запроса не будет установлена конечная точка, то есть она будет равна null, если текущий запрос не соответствует ни одному из маршрутов в EndpointMiddleware. То есть для определения конечной точки EndpointRoutingMiddleware использует маршруты из EndpointMiddleware.

Класс Endpoint не предоставляет много информации о конечной точке. В данном случае используется его свойство DisplayName, которое представляет имя конечной точки и обычно совпадает с маршрутом, обрабатываемым данной конечной точкой.

Куда больше информации предоставляет класс Microsoft.AspNetCore.Routing.RouteEndpoint, который наследуется от Endpoint и добавляет ряд дополнительных свойств. В частности, в данном случае применяется свойство RoutePattern.RawText. RoutePattern (объект одноименного класса RoutePattern) хранит всю информацию, ассоциированную с маршрутом конечной точки. Далее свойство RawText фактически хранит представление маршрута в текстовом виде, например, "/index" или "/".

В итоге, если конечная точка установлена, то выводим полученные данные и передаем управление следующему компоненту в конвейере, которым в данном случае является EndpointMiddleware.

var routePattern = (endpoint as Microsoft.AspNetCore.Routing.RouteEndpoint)?.RoutePattern?.RawText;

Debug.WriteLine($"Endpoint Name: {endpoint.DisplayName}");
Debug.WriteLine($"Route Pattern: {routePattern}");

// если конечная точка определена, передаем обработку дальше
await next();

Если конечная точка для обработки текущего запроса не определена, то завершаем обработку запроса, отправляя пользователю некоторое сообщение:

else
{
	Debug.WriteLine("Endpoint: null");
	// если конечная точка не определена, завершаем обработку
	await context.Response.WriteAsync("Endpoint is not defined");
}

Собственно в этом одно из преимуществ системы маршрутизации в ASP.NET Core 3+: даже если конечная точка не определена, мы можем при необходимости сформировать ответ пользователю, не передавая запрос далее - в EndpointMiddleware.

И в конце EndpointMiddleware определяет обработчики для двух маршрутов - "/index" и "/".

Запустим приложение и обратимся по адресу "/index" - то есть по первому маршруту в EndpointMiddleware:

EndpointMiddleware в ASP.NET Core

После этого в окне Output в Visual Studio мы можем увидеть диагностическую информацию:

Маршрутизация и EndpointMiddleware в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850