REST API в Yii2: чек-лист перед выкатом в прод
Что проверить в REST API на Yii2 до релиза: сериализация, аутентификация, лимиты запросов, версионирование и обработка ошибок. Разбор частых мест, где контроллеры отдают лишнее или падают под нагрузкой.

REST API на Yii2 разворачивается за десять минут: наследуешь контроллер от yii\rest\ActiveController, указываешь modelClass — и получаешь готовый CRUD с роутингом, форматированием и пагинацией. Проблемы начинаются, когда этот же контроллер уходит в прод: он отдаёт поля с паролями, не ограничивает частоту запросов, а на любую ошибку валидации возвращает HTML вместо JSON. Это чек-лист того, что стоит проверить в API на Yii2 (2.0.x) перед тем, как отдать его наружу.
Формат — практика: не пересказ документации, а разбор мест, где стандартное поведение фреймворка расходится с тем, что нужно на боевом сервере. Генерация OpenAPI-спеки и тестирование — отдельные темы, здесь только то, что влияет на безопасность и корректность ответов.
1. Сериализация: что реально уходит в ответ
ActiveController по умолчанию сериализует все атрибуты модели, которые вернёт fields(). Для ActiveRecord это все колонки таблицы. Если у пользователя есть password_hash, auth_key и access_token — они уедут клиенту вместе с именем и email.
Управляйте выдачей через методы модели fields() и extraFields(). Первый определяет поля, которые отдаются всегда, второй — те, что подгружаются только по параметру expand.
// модель User, Yii2 2.0.x
public function fields()
{
return [
'id',
'username',
'email',
// вычисляемое поле: ключ - имя в ответе, значение - замыкание
'createdAt' => function ($model) {
return date('c', $model->created_at);
},
];
}
public function extraFields()
{
// подтянется только при ?expand=profile
return ['profile'];
}
Проверка: запросите один объект и глазами пройдитесь по JSON. Ни password_hash, ни auth_key, ни внутренних служебных флагов там быть не должно. Не полагайтесь на то, что «клиент их не использует» — они всё равно в теле ответа.
Правило по умолчанию: белый список полей, а не чёрный. Перечислите то, что отдаёте, вместо того чтобы вычёркивать лишнее — иначе новая колонка в миграции автоматически станет публичной.
2. Аутентификация и её порядок
Аутентификатор подключается через поведение authenticator в методе behaviors() контроллера. Yii2 поддерживает несколько классов из коробки: HttpBearerAuth, QueryParamAuth, HttpBasicAuth. Метод findIdentityByAccessToken() в классе identity должен быть реализован — без него токен-аутентификация просто не найдёт пользователя.
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => \yii\filters\auth\HttpBearerAuth::class,
// не требовать токен на публичных экшенах
'except' => ['options'],
];
return $behaviors;
}
Две частые ошибки. Первая — забыть про optional или except для эндпоинтов, которые должны работать без токена (регистрация, публичный поиск). Вторая — CORS: если фронтенд на другом домене, браузер шлёт preflight-запрос OPTIONS, и если аутентификатор его не пропускает, вы получите ошибку ещё до основного запроса. Поведение corsFilter должно стоять раньше аутентификатора в массиве behaviors(), иначе OPTIONS завернётся на этапе проверки токена.
3. Ограничение частоты запросов
Yii2 умеет rate limiting, но не включает его сам. Механизм требует, чтобы identity-класс реализовывал интерфейс yii\filters\RateLimitInterface с тремя методами: getRateLimit(), loadAllowance() и saveAllowance(). Без их реализации фильтр rateLimiter ничего не ограничивает — молча пропускает всё.
loadAllowance() и saveAllowance() хранят состояние. Хранить его в колонках таблицы пользователей — рабочий вариант для небольших нагрузок, но при высокой частоте это лишние записи в основную БД на каждый запрос. Под нагрузкой состояние лучше держать в Redis — но это уже отдельная тема, здесь фиксируем сам факт: если RateLimitInterface не реализован, лимитов нет.
Проверка: в успешном ответе должны появиться заголовки X-Rate-Limit-Limit, X-Rate-Limit-Remaining и X-Rate-Limit-Reset. Если их нет — фильтр не работает.
4. Обработка ошибок и коды ответа
Самое частое расхождение с ожиданием: приложение в режиме debug отдаёт стектрейс, а без debug — обрезанную ошибку. Для API важно, чтобы ошибки всегда были в том же формате, что и успешные ответы (JSON), и с правильным HTTP-кодом.
- 422 — ошибки валидации модели. Yii2 сам возвращает этот код при неудачном
save()в стандартных экшенах, с массивом ошибок по полям. - 401 — не аутентифицирован (нет или невалиден токен).
- 403 — аутентифицирован, но нет прав (обычно из
checkAccess). - 404 — ресурс не найден; бросается через
NotFoundHttpException. - 405 — метод не разрешён для эндпоинта.
Убедитесь, что компонент errorHandler настроен на errorAction, либо что формат ответа принудительно JSON. Проще всего — повесить обработчик, который приводит любое исключение к JSON-ответу с полями name, message, code, status. Yii2 делает это сам, если Response::format установлен в FORMAT_JSON и включён фильтр ContentNegotiator.
5. Версионирование
Ломающее изменение в контракте API без версии — гарантированный сломанный клиент в проде. Yii2 не навязывает схему, но официальный гайд предлагает выносить версии в отдельные модули (v1, v2) с собственными неймспейсами. Это удобнее, чем разводить версии через заголовок Accept, потому что даёт физически разные классы моделей и контроллеров — старый v1 продолжает работать, пока вы правите v2.
| Подход | Плюс | Минус |
|---|---|---|
| Версия в URL (/v1/users) | Видно в логах и запросах, легко роутить | URL считается частью ресурса — формально «нечисто» |
| Версия в заголовке Accept | URL остаётся стабильным | Сложнее отлаживать, клиенты забывают заголовок |
| Модули Yii2 (v1, v2) | Разные классы, старая версия не ломается | Дублирование кода между версиями |
6. Пагинация и лимиты выборки
ActiveController отдаёт коллекции через ActiveDataProvider с пагинацией по умолчанию 20 записей на страницу. В ответе появляются заголовки X-Pagination-Total-Count, X-Pagination-Page-Count и ссылка Link. Клиент может передать ?per-page=500 — и если вы не ограничили верхнюю границу, один запрос вытянет полтаблицы в память.
Ограничьте pageSizeLimit у пагинации. Переопределите prepareDataProvider() в контроллере или задайте лимит в конфигурации Pagination. Значение вроде [1, 100] отсекает и ноль, и слишком большие страницы.
7. Только нужные HTTP-методы
ActiveController из коробки открывает index, view, create, update, delete и options. Если API читающий — отключите то, что не используется, через actions() с unset() или переопределите verbs(). Открытый DELETE на эндпоинте, о котором забыли, — типичная находка при аудите.
public function actions()
{
$actions = parent::actions();
// убираем удаление и обновление, оставляем чтение
unset($actions['delete'], $actions['update'], $actions['create']);
return $actions;
}
Короткий чек-лист перед выкатом
- Поля модели описаны белым списком в
fields(), служебных данных в ответе нет. - Аутентификатор подключён, публичные экшены в
except/optional. corsFilterстоит раньше аутентификатора,OPTIONSпроходит.- Rate limiting реально работает — заголовки
X-Rate-Limit-*в ответе. - Ошибки отдаются в JSON с корректными кодами 401/403/404/422.
- Версия зафиксирована в URL или модулях.
per-pageограничен сверху.- Лишние HTTP-методы отключены.
YII_DEBUGвыключен на проде — иначе стектрейсы уходят наружу.
Что делать дальше
Когда чек-лист пройден, следующий шаг — наблюдаемость. Логируйте коды ответов и время обработки по эндпоинтам: рост доли 5xx и p95-времени ответа скажет о проблеме раньше жалоб. Затем стоит зафиксировать контракт в OpenAPI-спецификации, чтобы клиенты и тесты опирались на неё, а не на живой сервер. Точные имена методов и опций сверяйте с официальным гайдом по REST в Yii2 для вашей минорной версии — детали поведения фильтров между релизами 2.0.x менялись.
Тендерный специалист 44-ФЗ / 223-ФЗ: Разбор PDF-документации закупки, поиск «заточек» под поставщика, чек-лист заявки.
Спросить за 20 ₽Источники: Yii2 Guide — RESTful Web Services, Yii2 Guide — Rate Limiting
Частые вопросы
У меня Yii 1.1, а не Yii2 — подойдёт этот чек-лист?
Rate limiting не работает — заголовков X-Rate-Limit нет, что проверить?
Модель всё равно отдаёт лишние поля, хотя я описал fields() — почему?
Грозит ли утечкой данных то, что YII_DEBUG остался включён?
Нужно ли отдельно защищать OPTIONS-запросы?
Можно ли смешивать несколько способов аутентификации на одном контроллере?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.