Главная » Разработка Битрикс » D7 » Bitrix\Main\Engine\Controller — AJAX-контроллеры D7

Bitrix\Main\Engine\Controller — AJAX-контроллеры D7

Схема AJAX-контроллера D7: фильтры проверяют запрос, действие возвращает данные в едином формате JSON

Bitrix\Main\Engine\Controller — штатный способ обрабатывать AJAX-запросы в новом ядре 1С-Битрикс. Вместо самописного файла с подключением пролога вы описываете класс с методами-действиями, а ядро берёт на себя проверку токена, разбор параметров и формирование ответа.

Разберём устройство контроллера, систему фильтров, возврат ошибок и способы вызова с фронтенда. Обзор остальных подходов к AJAX есть в статье AJAX в Битрикс.

Минимальный контроллер

<?php // /local/php_interface/classes/Controller/Catalog.php

namespace Project\Controller;

use Bitrix\Main\Engine\Controller;

class Catalog extends Controller
{
    public function getItemAction(int $id): array
    {
        return [
            'id'   => $id,
            'name' => 'Товар ' . $id,
        ];
    }
}

Два правила именования, без которых ничего не заработает:

  • метод-действие обязан заканчиваться на Action;
  • в вызове с фронтенда суффикс Action не указывается.

Регистрируем класс для автозагрузки в init.php:

<?php

use Bitrix\Main\Loader;

Loader::registerAutoLoadClasses(null, [
    'Project\\Controller\\Catalog' => '/local/php_interface/classes/Controller/Catalog.php',
]);

Параметры действия

Параметры приходят обычными аргументами метода — разбирать $_POST не нужно. Типы приводятся автоматически:

<?php

public function searchAction(string $query, int $limit = 10, bool $exact = false): array
{
    // $query, $limit и $exact уже нужного типа
    return ['found' => []];
}

Если обязательный параметр не передан, ядро само вернёт ошибку — до тела метода выполнение не дойдёт. Значения по умолчанию работают как в обычном PHP.

Нужна помощь с Битрикс?





    Фильтры

    Фильтры — проверки, выполняемые до действия. Именно они закрывают то, что в самописных обработчиках приходится писать руками.

    <?php
    
    use Bitrix\Main\Engine\Controller;
    use Bitrix\Main\Engine\ActionFilter;
    
    class Catalog extends Controller
    {
        protected function getDefaultPreFilters()
        {
            return [
                new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]),
                new ActionFilter\Csrf(),
                new ActionFilter\Authentication(),
            ];
        }
    }
    ФильтрЧто проверяет
    HttpMethodРазрешённый метод запроса
    CsrfТокен сессии — защита от подделки запроса
    AuthenticationПользователь авторизован
    ScopeОткуда допустим вызов: AJAX, REST
    ContentTypeТип содержимого запроса

    Фильтры для отдельных действий

    Если одно действие должно быть доступно без авторизации, а остальные — нет, описывается конфигурация действий:

    <?php
    
    public function configureActions()
    {
        return [
            'getItem' => [
                'prefilters' => [
                    new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_GET]),
                ],
            ],
        ];
    }

    Указанный здесь набор заменяет фильтры по умолчанию для этого действия. Пустой массив 'prefilters' => [] отключает все проверки — применяйте это осознанно.

    Ошибки

    Ошибки возвращаются не исключением, а через накопитель:

    <?php
    
    use Bitrix\Main\Error;
    
    public function addToCartAction(int $productId, int $quantity): ?array
    {
        if ($quantity <= 0) {
            $this->addError(new Error('Количество должно быть больше нуля', 'BAD_QUANTITY'));
            return null;
        }
    
        if (!$this->checkProductExists($productId)) {
            $this->addError(new Error('Товар не найден', 'NOT_FOUND'));
            return null;
        }
    
        return ['status' => 'added'];
    }

    Второй аргумент — код ошибки. Он удобен на клиенте: по нему можно различать ситуации, не разбирая текст сообщения.

    Формат ответа

    Контроллер всегда отдаёт одинаковую структуру. При успехе:

    {
        "status": "success",
        "data": { "status": "added" },
        "errors": []
    }

    При ошибке:

    {
        "status": "error",
        "data": null,
        "errors": [
            { "message": "Товар не найден", "code": "NOT_FOUND" }
        ]
    }

    Предсказуемый формат — одно из главных преимуществ подхода: клиентский код пишется один раз и работает со всеми действиями.

    Вызов с фронтенда

    Через BX.ajax.runAction

    BX.ajax.runAction('project:controller.catalog.getItem', {
        data: { id: 42 }
    }).then(function (response) {
        console.log(response.data);
    }, function (response) {
        response.errors.forEach(function (error) {
            console.error(error.code, error.message);
        });
    });

    Имя действия собирается из пространства имён, пути к классу и имени метода без суффикса.

    Через обычный fetch

    Если подключать библиотеку ядра не хочется, действие вызывается напрямую:

    const formData = new FormData();
    formData.append('id', '42');
    formData.append('sessid', BX.bitrix_sessid());
    
    fetch('/bitrix/services/main/ajax.php?action=project:controller.catalog.getItem', {
        method: 'POST',
        body: formData
    })
        .then(response => response.json())
        .then(result => console.log(result));

    Токен сессии здесь передаётся вручную — иначе фильтр Csrf отклонит запрос.

    Контроллер внутри компонента

    Если действия относятся к конкретному компоненту, контроллеры описываются в файле ajax.php рядом с class.php, а вызов адресуется через имя компонента. Так логика остаётся рядом с тем, к чему относится. Устройство компонентов разобрано в статье Создание компонента в Битрикс.

    Отладка

    Что проверять, если действие не вызывается:

    1. Имя действия. Пространство имён в нижнем регистре, суффикс Action не указывается.
    2. Автозагрузка класса. Если класс не зарегистрирован, ядро его не найдёт.
    3. Метод запроса. При фильтре HttpMethod с POST запрос GET будет отклонён.
    4. Токен сессии. Самая частая причина отказа при вызове через fetch.
    5. Ответ во вкладке «Сеть». В errors обычно написано, что именно не так.

    Частые ошибки

    • Суффикс Action указан в вызове. Действие не находится.
    • Все фильтры отключены «чтобы заработало». Обработчик становится доступен кому угодно с любого сайта.
    • Исключения вместо addError(). Клиент получает не JSON, а страницу ошибки.
    • Вывод внутри действия. Любой echo ломает разбор ответа.
    • Нет проверки прав в действии. Фильтр Authentication проверяет только факт авторизации, но не право на конкретную операцию.

    Итог

    Контроллеры D7 закрывают всё, что в самописных AJAX-обработчиках приходится делать вручную: проверку токена, ограничение метода запроса, разбор и приведение параметров, единый формат ответа с ошибками.

    Минимальный рабочий набор — класс-наследник Controller, метод с суффиксом Action, регистрация в автозагрузке и фильтры Csrf с HttpMethod. Отключать фильтры ради того, чтобы запрос прошёл, не стоит: именно они и есть основная ценность этого механизма.

    Нужна помощь с Битрикс?

    Исправим ошибку, доработаем сайт, ускорим Битрикс или поможем разобраться с проблемой.

    Услуги
    Инструменты
    База знаний