Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Рассмотрим, как происходит работа с маршрутами. При удачном сопоставлении адреса URL определенному маршруту у объекта RouteContext устанавливаются свойства
Handler и RouteData. Как мы посмотрели ранее, через свойство Handler
устанавливается и потом
вызывается делегат, который обрабатывает запрос по маршруту.
А свойство RouteData
предназначено для хранения информации о маршруте и его значениях. Оно представляет объект одноименного класса RouteData,
который имеет ряд свойств, содержащих информацию об обрабатываемом маршруте:
Values представляет словарь значений маршрута. Эти значения получены с помощью сегментирования строки запроса URL. При обработке мы можем использовать эти значения.
DataTokens содержит набор дополнительных данных, которые связаны с обрабатываемым маршрутом. Однако если данные в
RouteData.Values должны быть легко преобразованы в строки и обратно, то в RouteData.DataTokens
могут быть данные любых типов.
Routers хранит список маршрутов, которые использовались для успешного сопоставления с запросом. Первый объект в этом списке представляет коллекцию маршрутов, которая применяется для генерации URL. А последний элемент этого списка - собственно тот маршрут, который совпал с запросом.
И все эти данные мы можем получить при обрабтке запроса. Однако делегат RequestDelegate, который обрабатывает запрос в качестве параметра принимает не объект RouteContext, а объект HttpContext. Например:
private async Task Handle(HttpContext context) { await context.Response.WriteAsync("Hello ASP.NET Core"); }
Но у объекта HttpContext есть метод расширения GetRouteData(), который позволяет получить объект RouteData:
RouteData routeData = context.GetRouteData();
Для получения отдельных данных из словаря RouteData.Values также можно использовать еще один метод класса HttpContext - метод context.GetRouteValue()
string controller = context.GetRouteValue("controller").ToString();
Например, получим параметры запроса:
using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Routing; namespace RoutingApp { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddRouting(); } public void Configure(IApplicationBuilder app) { var myRouteHandler = new RouteHandler(Handle); var routeBuilder = new RouteBuilder(app, myRouteHandler); routeBuilder.MapRoute("default", "{action=Index}/{name}-{year}"); app.UseRouter(routeBuilder.Build()); app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } private async Task Handle(HttpContext context) { var routeValues = context.GetRouteData().Values; var action = routeValues["action"].ToString(); var name = routeValues["name"].ToString(); var year = routeValues["year"].ToString(); await context.Response.WriteAsync($"action: {action} | name: {name} | year:{year}"); } } }
Чтобы добавить новые маршруты, нам надо последовательно вызвать несколько методов routeBuilder.MapRoute()
, передав в них все необходимые параметры.
Например:
var myRouteHandler = new RouteHandler(Handle); var routeBuilder = new RouteBuilder(app, myRouteHandler); routeBuilder.MapRoute("default", "{action=Index}/{name}-{year}"); routeBuilder.MapRoute("default2", "{controller}/{action}/{id?}"); app.UseRouter(routeBuilder.Build());
Теперь если запрос соответствует первому маршруту, то он выбирается для обработки, и другие маршруты не учитываются. Если же запрос не соответствует маршруту, то система маршрутизации сопоставляет запрос со следующими маршрутами.
При определении маршрутов важно их определять так, чтобы у системы маршрутизации не возникало неоднозначности, какой маршрут использовать. Возьмем два маршрута, определенные выше, и запрос http://localhost:5634/index/lumia-2015. На первый взгляд он соответствует первому маршруту. И параметры маршрутизации распределятся следующим образом:
action | index |
name | lumia |
year | 2015 |
Но этому же запросу также соответствует и второй маршрут, так как этот маршрут соответствует трехсегментному запросу, в котором последний сегмент необязателен:
controller | index |
action | lumia-2015 |
Причем система маршрутизации сопоставляет маршруты с запросом в том порядке, в котором они определены. Поэтому если мы определяем маршруты, которые могут пересекаться, то вначале надо определять те маршруты, которые имеют больший приоритет или более специфичные. А больше общие или стандартные маршруты должны определяться ближе к концу.