В основе работы маршрутизации в ASP.NET Core лежит процесс URL matching или составление запрошенного адреса URL с конечной точкой. Данный процесс основывается на пути запроса и полученных в запросе заголовках. Данный процесс проходит ряд этапов:
Сначала выбираются все конечные точки, шаблон маршрута которых совпадает с путем запроса
Далее из полученного на предыдущем этапе набора конечных точек удаляются те, которые не соответствуют ограничениям маршрута
Затем из полученного на предыдущем этапе набора конечных точек удаляются те, которые не удавлетворяют политике объекта MatcherPolicy (вкратце: класс MatcherPolicy позволяет определить порядок сравнения конечных точек и адреса URL)
И в самом конце применяется объект EndpointSelector для выбора из полученного на предыдущем этапе списка конечной точки, которая в конечном счете и будет обрабатывать запрос
Приоритет конечных точек зависит от двух факторов:
Порядок следования в наборе конечных точек
Приоритетность шаблона маршрута
Приоритетность шаблонов маршрута зависит от специфичности шаблона. Специфичность шаблона определяется на основе следующих критериев:
Шаблон маршрута с большим количеством сегментов более специфичен, чем шаблон меньшим количеством сегментов
Сегмент с текстовым литералом (статический сегмент) более специфичен, чем сегмент с параметром маршрута
Сегмент с параметром, к которому применяется ограничение маршрута, более специфичен, чем сегмент с параметром без ограничения
Комплексный сегмент более специфичен, чем сегмент с параметром с ограничением
Параметр catch-all (параметр, который соответствует неопределенному количеству сегментов) наименее специфичен
Если в конечном счете осталось две и более конечных точек, которые соответствуют запрошенному адресу, и соответственно система маршрутизации не может выбрать, какая из этих конечных точек должна обрабатывать маршрут, то генерируется исключение.
Например, пусть у нас есть два шаблона маршрута: "/hello" и "/{message}".
Imports Microsoft.AspNetCore.Builder Imports Microsoft.AspNetCore.Http Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) Dim app = builder.Build() app.Map("/hello", Function(context As HttpContext) "Hello METANIT.COM") app.Map("/{message}", Function(message As String) $"Message: {message}") app.Map("/", Function(context As HttpContext) "Index Page") app.Run() End Sub End Module
Оба этих маршрута соответствуют пути запроса "/hello".
Однако шаблон маршрута "/{message}" более общий, так как параметр message
может передавать какое угодно значение. Тогда как шаблон "/hello"
состоит из статического сегмента и соответственно
более конкретный, более специфичный, поэтому конечная точка этого шаблона маршрута и будет выбрана для обработки запроса по пути "/hello".
Другой пример:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.AspNetCore.Http Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) Dim app = builder.Build() app.Map("/{message?}", Function(message As String) $"Message: {message}") app.Map("/", Function(context As HttpContext) "Index Page") app.Run() End Sub End Module
Здесь первый шаблон маршрута применяет необязательный параметр message. Второй шаблон маршрута соответствует корню веб-приложения. И в принципе оба этих шаблона соответствуют пути запроса "/". Однако второй шаблон представляет статический сегмент, поэтому его конечная точка будет выбрана в конечном счете для обработки запроса:
Более сложный пример:
Imports Microsoft.AspNetCore.Builder Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) Dim app = builder.Build() app.Map("/{controller}/Index/5", Function (controller As String) $"Controller: {controller}") app.Map("/Home/{action}/{id}", Function(action As String) $"Action: {action}") app.Run() End Sub End Module
Здесь опять же поскольку во втором маршруте первый сегмент представляет статический сегмент, то именно вторая конечная точка будет выбираться для обработки маршрута: