Маршрутизация и шаблоны URL

Маршрутизация: как URL превращается в вызов конкретного метода.

Суть: маршрутизация (routing) — это сопоставление входящего URL и HTTP-метода с конкретным обработчиком. Шаблоны маршрутов с параметрами, ограничениями и атрибутами определяют, какой код выполнится.

Когда приходит GET /api/users/42, фреймворк должен понять: какой метод вызвать и что такое 42. За это отвечает система маршрутизации. В ASP.NET Core используют атрибутную маршрутизацию: путь пишут прямо над методом.

Шаблоны и параметры

[HttpGet("{id:int}")]              // только целые: /users/42
public IActionResult Get(int id) => Ok(id);

[HttpGet("search")]                // /users/search?name=Аня
public IActionResult Search([FromQuery] string name) => Ok(name);

[HttpGet("{id:int}/orders/{orderId:int}")]
public IActionResult GetOrder(int id, int orderId) => Ok(new { id, orderId });

В фигурных скобках — параметры маршрута. Через двоеточие задают ограничения: {id:int} сработает только для чисел, {slug:alpha} — для букв. Если ограничение не выполнено, маршрут не совпадёт и пойдёт поиск дальше.

Путь запроса целиком

GET /api/users/42
        |
        v
[Routing] подобрать шаблон
        |
   "api/users/{id:int}"  -> совпало, id=42
        |
        v
[Model binding] 42 -> int id
        |
        v
[UsersController.Get(42)]
        |
        v
   Ok(...) -> HTTP 200 + JSON

Как работает под капотом

Маршрутизация в ASP.NET Core — это два middleware: UseRouting (определяет, какой эндпоинт подходит) и MapControllers (выполняет его). Между ними можно вставить аутентификацию и авторизацию — они уже знают выбранный эндпоинт и его атрибуты. Если несколько маршрутов подходят, выбирается наиболее специфичный; при неоднозначности фреймворк бросает ошибку. Параметры из URL преобразуются в типы метода через model binding — об этом подробнее в разделе про Web API.

Частые ошибки

  • Конфликт маршрутов. Два метода с одинаковым шаблоном и методом дадут ошибку неоднозначности при запуске.
  • Забыть про ограничения типов. Без :int строка /users/abc попадёт в метод и упадёт при биндинге.
  • Путать параметры маршрута и query. {id} — из пути, ?name= — из строки запроса ([FromQuery]).

Best practices

  • Используйте ограничения (:int, :guid) — это и валидация, и защита от лишних попаданий в метод.
  • Делайте URL ресурсо-ориентированными: /api/users/42/orders, а не /api/getUserOrders?u=42.
  • Именуйте маршруты (Name = "GetUser"), если нужно генерировать ссылки на них.

Ограничения, опциональные параметры и значения по умолчанию

Шаблоны маршрутов выразительнее, чем кажется на первый взгляд. Помимо :int и :guid есть :alpha (только буквы), :min(1) и :max(100) (границы числа), :length(5) (длина строки), :regex(...) (произвольное правило). Параметр можно сделать необязательным через ? ({id:int?}) или задать значение по умолчанию ({page:int=1}). Эти ограничения — не только валидация: они помогают маршрутизатору различать похожие маршруты и сразу отсекать заведомо неподходящие URL.

Когда несколько маршрутов потенциально подходят под один URL, ASP.NET Core выбирает наиболее специфичный по системе приоритетов: литеральные сегменты важнее параметров, параметры с ограничениями важнее без них. Если же возникает настоящая неоднозначность (два равнозначных маршрута), фреймворк бросает исключение при старте — это намеренно, чтобы вы исправили конфликт, а не получали случайное поведение в рантайме.

Почему routing стоит между middleware

Разделение на UseRouting и выполнение эндпоинта неслучайно. После UseRouting уже известно, какой эндпоинт будет вызван и какие у него атрибуты, но сам он ещё не выполнен. В этот промежуток встраиваются UseAuthentication и UseAuthorization: авторизация читает атрибуты выбранного эндпоинта (например [Authorize(Roles="Admin")]) и решает, пускать ли запрос дальше. Если бы routing выполнял эндпоинт сразу, авторизация не успела бы вмешаться.

Эта архитектура — пример того, как порядок middleware и этап маршрутизации работают вместе. Понимание, что «выбор эндпоинта» и «его выполнение» — разные моменты конвейера, помогает осознанно размещать собственную сквозную логику и не удивляться, почему авторизация видит метаданные маршрута.

Итог: маршрутизация сопоставляет URL и HTTP-метод с action через шаблоны и ограничения. Дальше — как из ответа формируется правильный HTTP-результат.

Проверьте себя
1. Что делает ограничение :int в шаблоне маршрута?
AПреобразует любую строку в число
BМаршрут совпадёт только если сегмент — целое число
CДелает параметр необязательным
DБерёт значение из тела запроса
2. Какие два middleware отвечают за маршрутизацию?
AUseCors и UseAuth
BUseRouting (выбор эндпоинта) и MapControllers (выполнение)
CUseStatic и UseSession
DUseHttps и UseHsts