Маршрутизация и шаблоны 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-результат.