Как в ASP.NET Core определяется, какой Action контроллера использовать для обработки входящего HTTP-запроса?

Ответ

Выбор действия (Action) — это процесс маршрутизации запросов (Routing), который сопоставляет входящий HTTP-запрос с исполняемым методом контроллера. Механизм работает на основе соглашений или, чаще, атрибутов маршрутизации.

Ключевые критерии сопоставления:

  1. HTTP-метод: Атрибуты [HttpGet], [HttpPost], [HttpPut] и т.д.
  2. Шаблон маршрута (Route Template): Определяется в [Route("...")] на контроллере или действии.
  3. Имя контроллера и действия (при использовании соглашений).
  4. Параметры запроса (из сегментов пути, строки запроса или тела).

Пример 1: Маршрутизация на основе атрибутов (рекомендуемый подход)

[ApiController]
[Route("api/[controller]")] // Базовый путь: /api/products
public class ProductsController : ControllerBase
{
    // GET /api/products
    [HttpGet]
    public IActionResult GetAll() { /* ... */ }

    // GET /api/products/5
    [HttpGet("{id:int}")] // Ограничение типа параметра
    public IActionResult GetById(int id) { /* ... */ }

    // POST /api/products/search
    [HttpPost("search")]
    public IActionResult Search([FromBody] SearchFilter filter) { /* ... */ }
}

Пример 2: Соглашения (Convention-based routing) в Program.cs

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
// Запрос /Products/Details/3 будет направлен в ProductsController.Details(3)

Процесс выбора:

  1. Промежуточное ПО маршрутизации (app.UseRouting()) анализирует URL запроса.
  2. Создается набор кандидатов — действий, чьи шаблоны и HTTP-методы совпадают.
  3. Из кандидатов выбирается наиболее специфичное действие (с большим количеством сегментов и ограничений).
  4. Если найдено несколько одинаково специфичных действий, возникает AmbiguousActionException.
  5. После выбора действия выполняется привязка модели (Model Binding) для параметров.

Ответ 18+ 🔞

Давай разберём эту вашу маршрутизацию, а то как будто в трёх соснах пиздеца ищешь. Сидит запрос, бедолага, в интернетах болтается, и ему надо понять, в какой метод контроллера ткнуть носом. Весь этот цирк называется выбор действия (Action Selection).

Если по-простому, то это такой умный дворник, который смотрит на URL, смотрит на тип запроса (GET, POST и прочая хуйня) и решает, какую функцию в твоём коде вызвать. Работает он либо по старым дедовским соглашениям, либо по атрибутам — что, честно говоря, в разы удобнее и понятнее.

На что этот дворник-маршрутизатор смотрит, чтобы не облажаться:

  1. HTTP-метод: Это типа [HttpGet], [HttpPost], [HttpPut]. Если запрос пришёл POST-ом, а метод помечен только [HttpGet] — нихуя не выйдет, мимо.
  2. Шаблон пути (Route Template): Прописывается в [Route("...")]. Это как адрес на конверте. "Иди на такой-то путь, сынок".
  3. Имя контроллера и действия: Если используешь старые соглашения, а не атрибуты.
  4. Параметры запроса: Вытаскиваются из пути, из строки запроса или прямо из тела.

Пример 1: Нормальный, человеческий способ через атрибуты (делай так!)

[ApiController]
[Route("api/[controller]")] // Значит, базовый путь будет /api/products
public class ProductsController : ControllerBase
{
    // GET /api/products
    [HttpGet]
    public IActionResult GetAll() { /* ... */ }

    // GET /api/products/5
    [HttpGet("{id:int}")] // Смотри, какая красота! Ограничение, что id только int. Не int — 404, пошёл нахуй.
    public IActionResult GetById(int id) { /* ... */ }

    // POST /api/products/search
    [HttpPost("search")]
    public IActionResult Search([FromBody] SearchFilter filter) { /* ... */ }
}

Пример 2: Старый, дедовский способ через соглашения (Program.cs)

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
// Расшифровываю: запрос /Products/Details/3 полетит прямиком в ProductsController.Details(3)

А теперь, как это всё работает под капотом, чтоб ты понимал:

  1. Запрос приплывает, и промежуточное ПО маршрутизации (app.UseRouting()) начинает его разглядывать, как идиот иконку.
  2. Оно собирает всех "кандидатов" — те методы контроллеров, у которых шаблон пути и HTTP-метод хоть как-то подходят.
  3. Из этой толпы выбирается самый придирчивый и уточнённый кандидат. У кого в шаблоне больше сегментов и ограничений — тот и красавчик.
  4. Если вдруг нашлось два метода, которые одинаково хороши, фреймворк ахуевает и кидает AmbiguousActionException. Сам виноват, распиздяй, сделай правила чёткими.
  5. Ну а когда действие выбрано, начинается магия привязки модели (Model Binding) — подтягиваются параметры из запроса в аргументы метода.

Вот и вся магия. Не так страшен чёрт, как его малюют. Главное — атрибуты используй и шаблоны пиши понятно, а то сам потом в своей же логике запутаешься.