Первый эндпоинт на Minimal API
Minimal API: первый рабочий эндпоинт за пять строк и аналогия конвейера на Python.
Суть: Minimal API — это способ описывать HTTP-эндпоинты прямо в
Program.cs, без контроллеров. Идеален для небольших сервисов, микросервисов и обучения: меньше церемоний, выше производительность.
Minimal API хорош для маленьких сервисов, но в большом приложении начиная с .NET 6 появились Minimal API: вы пишете app.MapGet("/path", () => "результат") — и эндпоинт готов. В .NET 8/9 Minimal API дозрели до продакшена: есть валидация, типизированные результаты, группировка маршрутов.
Первый эндпоинт
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Привет, мир!");
app.MapGet("/users/{id}", (int id) =>
new { Id = id, Name = "Аня" });
app.Run();
Первый эндпоинт на корне возвращает строку. Второй принимает id из URL (model binding сам преобразует строку в int) и возвращает объект — ASP.NET Core автоматически сериализует его в JSON. Запустите dotnet run и откройте /users/5 — получите {"id":5,"name":"Аня"}.
HTTP-методы
Каждому HTTP-глаголу — свой Map-метод: MapGet (чтение), MapPost (создание), MapPut (полное обновление), MapDelete (удаление). Это основа REST, к которой мы вернёмся в разделе про Web API.
Как работает под капотом
Когда приходит запрос GET /users/5, система маршрутизации сопоставляет путь с шаблоном /users/{id}, извлекает id = "5", преобразует в int и вызывает вашу функцию-обработчик. Возвращённый объект проходит через сериализатор System.Text.Json и уходит клиенту с заголовком Content-Type: application/json. Концептуально конвейер обработки — это цепочка функций, каждая из которых может что-то сделать с запросом и передать дальше. Смоделируем эту идею на Python.
# Аналог конвейера middleware: цепочка обработчиков запроса
def logging(request, nxt):
print(f"-> {request['method']} {request['path']}")
response = nxt(request)
print(f"<- {response['status']}")
return response
def routing(request, nxt):
if request["path"] == "/users/5":
return {"status": 200, "body": {"id": 5, "name": "Аня"}}
return {"status": 404, "body": "Not Found"}
def build_pipeline(middlewares, final):
def chain(i):
if i >= len(middlewares):
return final
return lambda req: middlewares[i](req, chain(i + 1))
return chain(0)
pipeline = build_pipeline([logging], routing)
print(pipeline({"method": "GET", "path": "/users/5"}))
Попробуй сам ▶ — здесь logging оборачивает routing: сначала логирует запрос, потом передаёт дальше, потом логирует ответ. Ровно так в ASP.NET Core middleware вызывают next().
Частые ошибки
- Возвращать строку там, где нужен статус-код. Для контроля кода ответа используйте
Results.Ok(),Results.NotFound(),Results.Created(). - Логика прямо в лямбде растёт до сотен строк. Выносите обработчики в отдельные методы/классы, когда они усложняются.
- Путать порядок параметров и источников. По умолчанию простые типы берутся из маршрута/строки запроса, сложные — из тела.
Best practices
- Группируйте связанные маршруты через
app.MapGroup("/users")— чище и можно навесить общие фильтры. - Возвращайте типизированные
Resultsдля корректных HTTP-кодов. - Minimal API — для небольших сервисов; для крупных с множеством действий удобнее контроллеры (следующий раздел).
Типизированные результаты и группировка
Возврат «голой» строки или объекта удобен для старта, но в реальном API лучше использовать Results и TypedResults: Results.Ok(dto), Results.NotFound(), Results.Created(uri, dto). Они задают корректный статус-код и одновременно несут информацию о типе — её подхватывает генератор OpenAPI, и документация получается точнее. Для группы связанных маршрутов есть MapGroup: вы пишете var users = app.MapGroup("/users") и дальше навешиваете эндпоинты и общие фильтры на эту группу.
В Minimal API параметры обработчика тоже разрешаются через DI: если в лямбду добавить параметр AppDbContext db или IUserService svc, контейнер подставит их автоматически, как и в контроллерах. Это стирает границу между «лёгким» Minimal API и «полноценными» контроллерами с точки зрения возможностей — отличается в основном стиль организации кода.
Minimal API против контроллеров: когда что
Оба подхода в .NET 8/9 production-ready и могут сосуществовать в одном проекте. Эмпирическое правило такое: Minimal API хорош для небольших и средних сервисов, BFF (backend-for-frontend), вебхуков, где ценится скорость и минимум церемоний. Контроллеры выигрывают в крупных приложениях с десятками действий, где важны единая структура, фильтры, конвенции и привычная организация по папкам. Выбор — вопрос масштаба и вкуса команды, а не «нового против старого».
Запускаемая врезка выше показала важную идею, которая красной нитью пройдёт через весь курс: обработка запроса — это цепочка обёрток. Каждое звено получает запрос, решает, что с ним сделать, и передаёт дальше через next. Эту же модель вы увидите в middleware, в фильтрах и в обработчиках авторизации — поняв её один раз, вы поймёте половину фреймворка.
Итог: Minimal API позволяет поднять рабочий эндпоинт за минуты. Вы увидели, как конвейер передаёт запрос по цепочке — эту модель мы углубим дальше.