Web API
Web API — это граница сервиса: место, где сырой HTTP превращается в типизированный вызов, а объект — обратно в статус и тело ответа. ASP.NET Core предлагает здесь две модели — Minimal APIs, где делегат вешается прямо на маршрут, и MVC-контроллеры с конвенциями, фильтрами и ModelState. Обе работают на одном и том же routing, одном контейнере и одном конвейере middleware; выбор между ними — это выбор объёма конвенций, а не отдельный фреймворк.
Все характерные ловушки темы — про то, кто принимает решение и когда. Привязка модели решает источник параметра по строгому порядку: явный атрибут [FromRoute]/[FromQuery]/[FromBody]/[FromHeader] побеждает всегда, а без него [ApiController] выводит источник сам — сложный тип из тела, простой из маршрута, затем из query. Тот же [ApiController] превращает невалидный ModelState в автоматический 400 с телом ProblemDetails ещё до входа в действие — а в Minimal API этого не происходит, потому что там нет ModelState. Аутентификация отвечает на вопрос «кто это», авторизация — «можно ли ему»: AddJwtBearer проверяет подпись, issuer, audience и срок против настроенных значений, кладёт ClaimsPrincipal в HttpContext.User и никого не запрещает; запрещает UseAuthorization. Разбор по слоям — ниже.
Карта темы
- Minimal APIs — делегат на маршруте,
TypedResults, отсутствиеModelStateи что именно теряется против контроллеров. - Результаты действий —
TпротивIActionResultпротивActionResult<T>, статусы,ProblemDetails. - Привязка модели — порядок источников, вывод
[ApiController], единственный параметр из тела, авто-400. - JWT-аутентификация — что именно проверяет
AddJwtBearerи где в конвейере это происходит. - Политики авторизации — требования и обработчики вместо имён групп, 401 против 403.
- Версионирование API — версионируется контракт, а не база; один явный селектор,
Sunsetи метрики по версиям. - Конфигурация API — типизированные опции, победа переменной окружения и где живёт ключ подписи.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Ждать автоматический 400 от [ApiController] в Minimal API | В Minimal API нет ModelState — невалидное тело спокойно доедет до обработчика, валидируйте сами |
| Считать, что Minimal API минует конвейер или контейнер | Это такой же endpoint: то же routing, тот же DI, тот же конвейер middleware |
| Ставить два сложных параметра в одно действие | Тело запроса читается ровно один раз — из тела связывается максимум один параметр |
Проверять ModelState.IsValid вручную в каждом действии под [ApiController] | Мёртвый код: 400 с ProblemDetails уже вернулся до входа в действие |
Возвращать IActionResult отовсюду | Тип полезной нагрузки исчезает из описания API — OpenAPI и клиентские генераторы видят «что-то» |
| Считать, что конкретный тип возврата не позволяет вернуть 404 | Позволяет ActionResult<T> — и типизированное тело, и статус |
| Верить issuer и audience, записанным внутри токена | Токен самоподтверждается — сверять надо с настроенными значениями, иначе проверка ничего не значит |
Думать, что payload JWT зашифрован | Он только подписан и читается любым, кто получил токен, — секретам в нём не место |
Регистрировать UseAuthorization раньше UseAuthentication | HttpContext.User ещё пуст — валидный токен даёт вечный 401 |
Ждать, что UseAuthentication сама отклонит анонимный запрос | Она только строит принципала; решение о доступе принимает UseAuthorization |
| Путать 401 и 403 | 401 — не аутентифицирован (кто ты?), 403 — аутентифицирован, но не имеет права |
| Зашивать бизнес-правила в имена ролей | Любое изменение оргструктуры превращается в правку кода и релиз; политика называет намерение, а не группу |
| Ломать контракт «на месте», рассчитывая, что клиенты успеют | Сторонние клиенты, которых вы не контролируете, ломаются молча — версионируйте контракт |
| Выбирать версию неявно — по API-ключу или «по умолчанию последнюю» | Ответ начинает зависеть от скрытого состояния; старый клиент однажды молча уезжает на новый контракт |
Класть ключ подписи JWT в appsettings.json | Файл коммитится — любой с доступом к репозиторию может выпускать токены для вашего API |
Значение для собеседований
По Web API проверяют не знание атрибутов, а понимание границы: где заканчивается HTTP и начинается ваш тип. Самый частый формат — «покажите действие и объясните, что произойдёт с таким запросом»: откуда свяжется каждый параметр, кто вернёт 400 при невалидном теле, какой статус увидит клиент и в какой момент отработал токен.
Что обычно проверяют:
- Что даёт
[ApiController]бесплатно — авто-400 сProblemDetails, вывод источников привязки, обязательный маршрут по атрибутам. - Порядок источников привязки модели и почему из тела связывается только один параметр.
- Когда возвращать
IActionResult, когда конкретный тип, а когдаActionResult<T>. - Что теряют Minimal APIs против контроллеров (и что не теряют — конвейер и контейнер).
- Что именно валидирует
AddJwtBearerи откуда берутся эти значения. - Разницу аутентификации и авторизации, смысл политики против роли и разницу 401 и 403.
- Как версионировать публичный API и как узнать, что старую версию уже можно убрать.
Типичный неверный ответ: «Токен валиден — значит, доступ разрешён». Валидная подпись говорит только о том, кто обратился: UseAuthentication построила ClaimsPrincipal и на этом остановилась. Разрешение — отдельное решение UseAuthorization по метаданным endpoint, и именно поэтому 401 (не знаем, кто ты) и 403 (знаем, но нельзя) — это два разных ответа.