Перейти к содержимому

AJAX-действия своего компонента - действия, параметры, ошибки

Принимаем запрос прямо в своём компоненте: объявление действий, вызов с клиента, подписанные параметры, фильтры проверки и возврат ошибок.

Что нужно знать заранее

Действие компонента - это способ обработать запрос там же, где живёт вывод. Отдельный контроллер и отдельный файл при этом не нужны, а параметры вызова остаются связанными с конкретным компонентом страницы.

При вызове действия основной метод компонента не выполняется вообще. Отрабатывают только подготовка параметров, подключение языковых сообщений и само действие, поэтому данные для него готовят прямо внутри действия.

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

Шаги

  1. Объявить у класса компонента интерфейс действий и метод настройки их фильтров.
  2. Написать сами действия отдельными публичными методами с нужным окончанием имени, по одному на операцию.
  3. Перечислить параметры компонента, которые должны доехать до действия подписанными и проверенными.
  4. Вызвать действие с клиента штатной функцией ядра, передав подписанные параметры и данные.
  5. Возвращать ошибки списком, а не исключением, и показывать их в интерфейсе.

Решение

Объявляем действия у класса компонента:

class VendorFavoriteComponent extends \CBitrixComponent
implements \Bitrix\Main\Engine\Contract\Controllerable, \Bitrix\Main\Errorable
{
public function configureActions(): array { return []; } // фильтры по умолчанию
public function addAction(int $productId): array
{
return ['count' => $this->addToFavorite($productId)]; // ответ уедет в JSON
}
}

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

Перечисляем параметры, доступные действию:

protected function listKeysSignedParameters(): array
{
return ['IBLOCK_ID', 'PATH_TO_DETAIL']; // ядро подпишет и проверит подпись
}
// в действии их читают методом получения проверенных параметров компонента

Подписанные параметры - это способ не доверять клиенту. Идентификатор инфоблока приходит с подписью ядра, и подмена значения в запросе приводит к отказу, а не к работе с чужими данными.

Вызываем действие с клиента:

BX.ajax.runComponentAction('vendor:catalog.favorite', 'add', {
mode: 'class', // действие лежит в классе компонента
signedParameters: signedParams, // строка подписи из шаблона компонента
data: { productId: 15 },
}).then((response) => {
document.querySelector('.js-fav-count').textContent = response.data.count;
});

Ответ действия приходит в поле данных, а не корнем ответа. Ошибки платформа кладёт отдельным списком, поэтому обработчик успеха и обработчик отказа пишут рядом, а не разбирают одно поле.

Возвращаем ошибку вместо исключения:

use Bitrix\Main\Error;
public function addAction(int $productId): ?array
{
if ($productId <= 0) {
$this->errorCollection[] = new Error('Товар не найден', 'NOT_FOUND');
return null; // ошибки уедут клиенту списком, а не пятисотой
}
return ['count' => $this->addToFavorite($productId)];
}

Исключение в действии превращается в ошибку сервера и белый экран у посетителя. Список ошибок доезжает до клиента как данные, и интерфейс показывает понятный текст вместо пустого места.

Настраиваем фильтры проверки:

public function configureActions(): array
{
return ['add' => ['prefilters' => [
new \Bitrix\Main\Engine\ActionFilter\Authentication(), // только авторизованным
new \Bitrix\Main\Engine\ActionFilter\HttpMethod([\Bitrix\Main\Engine\ActionFilter\HttpMethod::METHOD_POST]),
]]];
}

Фильтры выполняются до действия и отсекают неверные запросы дешёвым способом. Убирать проверку сеанса ради удобства не стоит: она и есть защита от запросов с чужих страниц.

Типичные проблемы

В действии нет данных, подготовленных компонентом.

Основной метод компонента при вызове действия не выполняется, и подготовка данных пропадает. Данные готовят внутри самого действия либо в методе подготовки параметров компонента.

Действие отвечает отказом с ошибкой проверки сеанса.

Запрос уходит без признака сеанса или методом, который фильтр не пропускает. Признак сеанса передаёт штатная функция вызова действия, если её не обходить.

Параметры компонента не доехали до действия.

Они не перечислены в списке подписываемых параметров класса, а иначе не передаются. Ядро передаёт в действие только явно перечисленные в классе ключи параметров.

Вместо текста ошибки посетитель видит пустой блок.

Действие выбрасывает исключение вместо того, чтобы вернуть список своих ошибок. Ошибки складывают в коллекцию класса и возвращают клиенту обычными данными.

Ответ приходит, но поля в нём не те.

Клиент читает корень ответа вместо вложенного поля с данными действия. Полезная нагрузка действия всегда лежит внутри поля данных общего ответа.

Частые вопросы

Когда брать действие компонента, а когда отдельный контроллер?

Действие удобно, когда запрос относится к конкретному компоненту на странице и его параметрам. Отдельный контроллер берут для общего API, не привязанного к выводу.

Как передать в действие идентификатор инфоблока?

Через список подписываемых параметров компонента, а не полем запроса. Так значение приходит с подписью ядра и не подделывается.

Можно ли вызвать действие из шаблона компонента?

Да, шаблон отдаёт клиенту строку подписи параметров, и она уходит в вызов. Обычно её кладут в атрибут корневого блока шаблона.

Как отладить действие?

Смотреть ответ во вкладке сети браузера и писать в журнал внутри действия. Отладчик тоже работает, но журнал быстрее на боевом стенде.

Нужно ли самому проверять права в действии?

Прикладные права - да, права доступа к странице проверяют фильтры. Проверку владельца данных всегда пишут в самом действии.

Смежное

Первоисточник