Модуль в /local/modules готов, форма ждёт ответ без перезагрузки - а в консоли Invalid csrf token или Could not find description in DefaultController. На практике это самая частая проблема: AJAX не работает, потому что запрос ушёл не в тот transport. В документации три пути: BX.ajax, runAction и runComponentAction. Ниже - сквозной сценарий для 1С-Битрикс: Управление сайтом: D7-контроллер, configureActions и BX.ajax.runAction. После чек-листа POST вернёт JSON status success в Network.
AJAX в CMS - это запрос к серверу без перезагрузки страницы. Современный путь: класс в lib/Controller/ модуля наследует Bitrix\Main\Engine\Controller, метод заканчивается на Action, на фронте вызываете BX.ajax.runAction('vendor:module.controller.method'). Ядро само проверяет HTTP-метод, авторизацию и CSRF-токен sessid. Пустой prefilters в configureActions снимает всю защиту - для гостевой формы используйте -prefilters, а не пустой массив.
Речь про коробочную CMS на вашем сервере, не про облачный портал Bitrix24. Если модуль ещё не собран - начните с гайда по созданию модуля: без установленного пакета и include.php контроллер просто не найдётся.
Выберите transport: runAction, runComponentAction, REST или legacy ajax.php
AJAX (асинхронный запрос) - когда браузер спрашивает сервер в фоне и обновляет только кусок страницы. В Битрикс для этого есть четыре подхода. Путают их чаще всего - отсюда и ошибка DefaultController, когда запрос уходит не туда.
| Задача | Инструмент | Где код | Фронт |
|---|---|---|---|
| Логика своего модуля: корзина, ЛК, API внутри сайта | Engine\Controller + runAction | lib/Controller/ в модуле (B69) | BX.ajax.runAction('vendor:module.order.create') |
| Кнопка в шаблоне компонента | Controllerable + runComponentAction | class.php компонента | BX.ajax.runComponentAction(...) |
| Интеграция с внешней системой по URL | REST API, вебхуки | rest, local/routes | fetch к /rest/... - см. гайд по REST |
| Старый сниппет с prolog_before.php | Отдельный ajax/handler.php | Любая папка | fetch или BX.ajax на свой URL |
Для AJAX внутри компонента без выноса в модуль - создание своего компонента и runComponentAction. Для красивого URL API, а не кнопки на странице - REST и настройка ЧПУ, не дублируйте urlrewrite-гайд здесь.
Делайте: для формы в своём модуле берите Engine\Controller и runAction. Не делайте: не тащите REST-вебхук туда, где хватит runAction - лишний transport усложнит отладку.
Создайте каркас контроллера в lib/Controller/
Контроллер - PHP-класс, который принимает AJAX-запрос и отдаёт JSON. Ядро ищет его по строке vendor:module.controller.method: vendor - ID модуля до точки, module - имя после точки, controller - имя файла без Controller, method - имя метода без суффикса Action. Например, для mycompany.shop и класса Order с createAction строка будет mycompany.shop:order.create.
- Добавьте .settings.php в корень модуля с defaultNamespace - ядро поймёт, где искать классы.
- Создайте lib/Controller/Order.php с namespace Mycompany\Shop\Controller.
- Унаследуйте класс от \Bitrix\Main\Engine\Controller.
- Назовите публичный метод createAction - суффикс Action обязателен.
- Подключите модуль через Loader::includeModule перед тестом.
- Проверьте путь mycompany.shop:order.create для класса Order и метода createAction.
// .settings.php модуля mycompany.shop
return [
'controllers' => [
'value' => [
'defaultNamespace' => '\\Mycompany\\Shop\\Controller',
],
'readonly' => true,
],
];
namespace Mycompany\Shop\Controller;
use Bitrix\Main\Engine\Controller;
class Order extends Controller
{
public function createAction(array $fields): array
{
return ['id' => 1, 'name' => $fields['name'] ?? ''];
}
}
Типичная ошибка Could not find description of shop.order.create in DefaultController: нет .settings.php, класс лежит не в lib/Controller/, или MODULE_ID не совпадает с первой частью строки runAction.
Делайте: держите контроллеры только в lib/Controller/ с PSR-4. Не делайте: не кладите класс в корень модуля - резолвер уйдёт в DefaultController.
Настройте configureActions: prefilters, CSRF и гостевые POST
configureActions - таблица правил для каждого экшена. По умолчанию ядро вешает три prefilters (предфильтры - проверки до выполнения метода): HttpMethod, Authentication, Csrf. CSRF (защита от подделки запроса) требует sessid в data POST-запроса.
use Bitrix\Main\Engine\ActionFilter\Authentication;
use Bitrix\Main\Engine\ActionFilter\Csrf;
use Bitrix\Main\Engine\ActionFilter\HttpMethod;
public function configureActions(): array
{
return [
'create' => [
'-prefilters' => [Authentication::class],
'+prefilters' => [
new HttpMethod([HttpMethod::METHOD_POST]),
],
],
];
}
Ключ prefilters с пустым массивом полностью заменяет стандартные фильтры - CSRF и авторизация исчезнут. Для гостевой формы снимайте только Authentication через -prefilters, Csrf на POST оставляйте.
Ошибка Access denied - экшен требует авторизованного пользователя. Invalid csrf token - в data не передали sessid: BX.bitrix_sessid().
Делайте: для мутаций оставляйте POST и Csrf. Не делайте: не пишите prefilters => [] ради быстрого фикса - откроете дыру.
Вызовите экшен с фронта через BX.ajax.runAction
BX.ajax.runAction - JS-обёртка над /bitrix/services/main/ajax.php. Она сама подставляет action и при просрочке csrf обновляет токен.
BX.ajax.runAction('mycompany.shop.order.create', {
data: {
fields: { name: 'Тест' },
sessid: BX.bitrix_sessid()
}
}).then(function (response) {
if (response.status === 'success') {
console.log(response.data);
} else {
console.error(response.errors);
}
});
В DevTools откройте Network, найдите ajax.php, проверьте Form Data: action=mycompany.shop.order.create, sessid совпадает с BX.bitrix_sessid(). Ответ: {"status":"success","data":{...},"errors":[]}. Ошибки в errors[].message - читайте code: invalid_csrf, ACCESS_DENIED.
На сервере для бизнес-ошибок используйте $this->addError(new Error('Текст', 'MY_CODE')) - фронт получит status error без PHP fatal.
Делайте: логируйте response.errors в консоль при отладке. Не делайте: не светите sessid в публичных логах.
Отладьте типичные ошибки в Network и консоли
| Сообщение | Причина | Что сделать |
|---|---|---|
| Could not find description ... DefaultController | Неверный namespace или путь класса | .settings.php, lib/Controller/, includeModule |
| Invalid csrf token | Нет sessid в POST | data.sessid = BX.bitrix_sessid() |
| Access denied | Authentication не снят для гостя | -prefilters Authentication, Csrf оставить |
| Could not find value for parameter | Имя поля в data не совпало с аргументом Action | fields в data для array $fields |
Legacy ajax.php с prolog_before.php и check_bitrix_sessid() работает, но каждый обработчик пишете вручную. Миграция: перенесите логику в *Action, замените fetch на runAction, удалите дублирующий handler.php.
Делайте: сверяйте строку action с MODULE_ID и именем класса. Не делайте: не смешивайте runAction и runComponentAction в одной кнопке без явного выбора.
Получите рабочий AJAX-экшен: чек-лист перед продом
- Убедитесь, что POST-мутации идут только через HttpMethod POST.
- Проверьте sessid в каждом POST с фронта.
- Настройте configureActions: гостю - только -Authentication, не пустой prefilters.
- Прогоните Network: status success и понятные errors[].code.
- Закройте права: гостевой экшен не должен вызывать админ-логику.
- Сохраните в репозитории .settings.php и тестовый вызов runAction - чтобы команда повторила сценарий без угадываний.
Workflow: кнопка на странице → BX.ajax.runAction → /bitrix/services/main/ajax.php → Order::createAction → JSON в браузер.
Нужен разбор AJAX под ваш модуль - напишите через контакты. Похожие кейсы - в портфолио.
Автор: Максим Мольков, разработчик 1С-Битрикс.
Источники: справка BX.ajax.runAction, пре- и постфильтры BitrixFramework, урок "Контроллер" (LESSON_ID=6436).
Частые вопросы
Чем runAction отличается от runComponentAction?
runAction вызывает Engine\Controller в модуле по строке vendor:module.controller.method. runComponentAction - экшены class.php компонента с Controllerable и signedParameters. Модульная логика - runAction, кнопка в шаблоне компонента - runComponentAction.
Зачем configureActions, если фильтры есть по умолчанию?
Дефолты подходят не всем: гостевая форма, только POST, отключение лишней проверки. configureActions задаёт правила точечно. Пустой prefilters снимает всё - используйте +prefilters и -prefilters.
Как убрать проверку CSRF для публичного AJAX?
Технически - снять Csrf через -prefilters, но для POST это дыра. Правильный путь: оставить Csrf и передавать sessid: BX.bitrix_sessid() в data. Снимать Csrf только для осознанных GET-чтений без мутаций.
Где физически лежит контроллер?
В /local/modules/vendor.module/lib/Controller/Имя.php. Namespace пропишите в .settings.php controllers.defaultNamespace. Без этого ядро ищет класс в DefaultController и падает с Could not find description.
Чем AJAX-контроллер отличается от REST API?
runAction - внутренний transport CMS для страниц сайта, endpoint ajax.php. REST - внешние интеграции по /rest/ с приложениями и вебхуками. Для кнопки в ЛК берите runAction, для CRM снаружи - REST из отдельного гайда.
Нужен ли local/routes для runAction?
Нет. runAction ходит в /bitrix/services/main/ajax.php. local/routes нужен для своих URL и REST-подобных маршрутов - это тема ЧПУ и urlrewrite, не базовый AJAX-экшен.