Маршрутизація (Routing) — це фундаментальний механізм будь-якого веб-фреймворку. Вона відповідає за аналіз вхідного HTTP-запиту (наприклад, GET /users/1) та виклик відповідної ділянки коду (обробника) для формування відповіді.
У цьому розділі ми розберемо анатомію маршрутизації в ASP.NET Core Minimal APIs, навчимося створювати кінцеві точки (Endpoints) та працювати з параметрами маршрутів.
Уявіть великий офісний центр. Коли ви заходите всередину, ви бачите турнікет і рецепцію зі списком компаній на різних поверхах. HTTP-запит — це ви. Маршрутизатор (Router) — це рецепція. Вона дивиться куди вам треба (/sales або /support) і направляє вас до потрібного кабінету (обробника).
Без маршрутизації нам довелося б вручну писати складний код з купою if/else, щоб аналізувати URL кожного запиту і вирішувати, що робити.
В ASP.NET Core маршрутизація складається з двох основних етапів (Middleware):
UseRouting()): Знаходить кінцеву точку, яка відповідає запиту.UseEndpoints()): Виконує код (делегат) знайденої кінцевої точки.UseRouting та UseEndpoints вручну, оскільки клас WebApplication автоматично налаштовує їх для вас.Кінцева точка (Endpoint) складається з трьох основних речей:
/ або /users/{id}).Для визначення маршрутів ми використовуємо методи об'єкта app (інстанс WebApplication), такі як MapGet, MapPost, MapPut, MapDelete.
Розглянемо найпростіший приклад:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Головна сторінка");
app.MapGet("/about", () => "Сторінка про компанію");
app.Run();
Анатомія коду:
/ і повертає рядок "Головна сторінка"./about і повертає "Сторінка про компанію".() => ....Сучасні API рідко повертають просто текст. Зазвичай це структуровані дані у форматі JSON. ASP.NET Core Minimal APIs робить перетворення об'єкта в JSON автоматично!
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/user", () => new Person("John Doe", 30));
app.Run();
record class Person(string Name, int Age);
Анатомія коду:
record class Person./user ми просто повертаємо new Person(...).Content-Type: application/json.Результат запиту:
{
"name": "John Doe",
"age": 30
}
Використовуючи різні HTTP-методи (MapGet, MapPost, MapPut, MapDelete), ми можемо побудувати повноцінне RESTful API:
var app = WebApplication.Create();
app.MapGet("/items", () => "Отримання списку");
app.MapPost("/items", (Item item) => Results.Created($"/items/{item.Id}", item));
app.MapPut("/items/{id}", (int id, Item item) => Results.NoContent());
app.MapDelete("/items/{id}", (int id) => Results.Ok());
app.Run();
public record Item(int Id, string Name);
Анатомія коду:
MapGet("/items"): Обробляє отримання всіх елементів.MapPost("/items"): Дані з тіла запиту (Request Body) автоматично десеріалізуються (JSON -> об'єкт) і передаються в параметр Item item.MapPut та MapDelete: Містять у шляху параметр {id}, про який ми поговоримо детальніше нижче.Results.Created, Results.Ok, Results.NoContent: Це вбудовані хелпери для повернення правильних HTTP статус-кодів (201 Created, 200 OK, 204 No Content).Часто шлях містить динамічні значення. Наприклад, ми хочемо отримати користувача з ID 123 за адресою /users/123. Для цього ми використовуємо параметри маршрутів.
Параметри позначаються фігурними дужками {назва_параметра} всередині шаблону маршруту.
app.MapGet("/users/{id}", (int id) =>
{
return $"ID користувача: {id}";
});
Анатомія коду:
/users/{id} повідомляє роутеру, що після /users/ йде динамічна частина.int id.GET /users/42 -> результат: "ID користувача: 42".GET /users/hello, фреймворк не зможе конвертувати "hello" в int і автоматично поверне помилку 400 Bad Request.Шлях може містити стільки параметрів, скільки потрібно, а як роздільник зазвичай використовують символ / (хоча можна й інші, наприклад -).
app.MapGet("/users/{userId}/books/{bookId}", (int userId, int bookId) =>
{
return $"Користувач {userId} запитує книгу {bookId}";
});
Запит: GET /users/14/books/89 -> результат: "Користувач 14 запитує книгу 89".
Іноді параметр може бути відсутнім у URL. У такому випадку ми додаємо ? після імені параметра в шаблоні, а параметр у делегаті робимо nullable або задаємо дефолтне значення.
// Опціональний параметр
app.MapGet("/products/{category?}", (string? category) =>
{
return $"Категорія: {category ?? "Усі товари"}";
});
// Параметр зі значенням за замовчуванням
app.MapGet("/articles/{page=1}", (int page) =>
{
return $"Сторінка номер: {page}";
});
Анатомія коду:
/products/{category?}: Запит /products/electronics поверне "Категорія: electronics". Запит /products — поверне "Категорія: Усі товари". Зверніть увагу на Nullable тип string?./articles/{page=1}: Запит /articles встановить параметр page у значення 1.Важливо: Опціональні параметри та параметри зі значеннями за замовчуванням мають бути останніми сегментами шаблону URL.
Іноді під час розробки корисно побачити повний список зареєстрованих маршрутів. ASP.NET Core зберігає цю інформацію і дозволяє отримати до неї доступ через залежність IEnumerable<EndpointDataSource>.
app.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
{
var endpoints = endpointSources.SelectMany(source => source.Endpoints);
var routes = new List<string>();
foreach (var endpoint in endpoints)
{
if (endpoint is RouteEndpoint routeEndpoint)
{
routes.Add(routeEndpoint.RoutePattern.RawText);
}
}
return routes;
});
Анатомія коду:
IEnumerable<EndpointDataSource>. Це внутрішня колекція фреймворку, яка тримає інформацію про всі роути.RouteEndpoint, дістаємо його "сирий текст" (RoutePattern.RawText).string[].GET /add/{a}/{b}.
Вона повинна приймати два цілих числа як параметри маршруту і повертати їх суму у вигляді рядка "Сума: X".
Перевірте, як відреагує API, якщо замість числа передати текст.POST /pets, яка приймає JSON-об'єкт Pet (Name, Species, Age).
Збережіть його у static List<Pet> в пам'яті. Поверніть створеного улюбленця разом зі статусом 201 Created та URL /pets/ім'я-тварини.
Створіть GET /pets, щоб повертати весь поточний список.Міні-сервіс управління товарами Реалізуйте:
GET /productsGET /products/category/{category=all}GET /products/{id}Results.NotFound()).GET /routes, який виведе всю зареєстровану "мапу гілок", як було показано вище.Конвеєр запитів та Middleware
Вичерпний посібник з архітектури Pipeline в ASP.NET Core. Розбираємо Middleware (Use, Run, Map), HttpContext, Request та Response об'єкти.
Маршрутизація в ASP.NET Core: Розширені можливості
У попередньому матеріалі ми дізналися, як створювати базові маршрути та приймати параметри. Однак реальні проєкти вимагають значно більшого контролю над тим, які запити має обробляти конкретна кінцева точка.