Полный цикл в digital

BX.ajax.runComponentAction - функция для отправки аяксовых запросов в класс

С версии битрикса 17.5.10 добавился новый функционал по работе с аяксом. Теперь можно вызвать аяксом произвольный метод модуля или произвольный метод компонента без необходимости выполнения шаблона сайта. То есть по запросу выполнится только немного служебного кода и собственно сам метод.

На клиенте все довольно просто, мы должны отправить обычный аякс запрос по адресу /bitrix/services/main/ajax.php.

В js библиотеке битрикса есть методы которые самостоятельно формируют запрос, запись получается слегка короче, чем при написании запроса другими способами.

Пример Битриксовой функции для обращения к компоненту

Приведу код и jsdoc битриксовой функции. Из jsdoc можно узнать параметры, которые эта функция ожидает:

/**
    *
    * @param {string} component
    * @param {string} action
    * @param {Object} config
    * @param {?string} [config.analyticsLabel]
    * @param {?string} [config.signedParameters]
    * @param {string} [config.method='POST']
    * @param {string} [config.mode='ajax'] Ajax or class.
    * @param {Object} [config.data]
    * @param {?array} [config.headers]
    * @param {?number} [config.timeout]
    * @param {Object} [config.navigation]
    */
BX.ajax.runComponentAction = function (component, action, config)
{
    config = prepareAjaxConfig(config);
    config.mode = config.mode || 'ajax';
    
    var getParameters = prepareAjaxGetParameters(config);
    getParameters.c = component;
    getParameters.action = action;
    
    var url = BX.util.add_url_param('/bitrix/services/main/ajax.php', getParameters);
    
    return buildAjaxPromiseToRestoreCsrf({
        method: config.method,
        dataType: 'json',
        url: url,
        data: config.data,
        timeout: config.timeout,
        preparePost: config.preparePost,
        headers: config.headers
    });
};

Запрос в компонент к файлу class.php

Клиентская сторона

Разберем тестовый компонент:

  • my_components пространство имен
  • ajax компонент
  • test метод testAction() в файле class.php

Пример запроса на битриксовой библиотеке, при использовании BX.ajax.runComponentAction() сессия передается автоматически:

// делаем ajax запрос в компонент my_components:ajax к методу testAction()
var request = BX.ajax.runComponentAction('my_components:ajax', 'test', {
mode: 'class',
data: {
param1: 'asd',
sessid: BX.message('bitrix_sessid')
}
});
// промис в который прийдет ответ
request.then(function (response) {
console.log(response);
});

Примерно то же самое на jquery:

var query = {
c: 'my_components:ajax',
action: 'test',
mode: 'class'
};
var data = {
param1: 'asd',
sessid: BX.message('bitrix_sessid')
};
var request = $.ajax({
url: '/bitrix/services/main/ajax.php?' + $.param(query, true),
method: 'POST',
data: data
});
request.done(function (response) {
console.log(response);
});

Немного сложностей вносит использование csrf. Если кратко, то это защита от вызова метода на сторонних сайтах. Если вы хотите защитить запрос, то вам придется воспользоваться методом BX.message('bitrix_sessid') в js (в объекте data). Либо каким то другим образом передать с бэкенда на фронт данные функции <?echo bitrix_sessid();?> и подставить эту строку в sessid.

Если же вы никакие сколько нибудь значимые данные в запросах не передаете, то проверку csrf можно будет отключить на бэкенде (для каждого аякс метода независимо, для каких-то можно и оставить) и отправлять запросы без sessid.

Серверная сторона

На серверной стороне генерировать ответ будет обычный компонент. Так как в примере выше мы отправляли запросы к компоненту my_components:ajax, то фактически будет выполняться метод testAction в классе из файла /local/components/my_components/ajax/class.php, название класса в файле не важно. Обращаю ваше внимание, на клиенте мы спрашиваем просто test, а по факту выполняется метод testAction.

В методе testAction есть два аргумента: $param2 и $param1. Я намеренно поставил их в обратном порядке, чтобы показать как происходит маппинг данных из запроса. Если вы вернетесь в описание клиентской части, то увидите, что там передается param1, а param2 не передается. Битрикс проверяет имена переменных через php reflection и передает в аргументы метода ajax данные с тем же типом и названием. То есть другими словами не нужно задумываться какой у вас там аргумент первый, какой второй, как это все придет из браузера, в каком порядке, достаточно просто называть переменные одинаково на сервере и на клиенте.

Метод configureActions описывает пре и постфильтры для запроса. В примере выше, чтобы запрос выполнился пользователь должен быть авторизован (префильтр new Bitrix\Main\Engine\ActionFilter\Authentication()), запрос должен идти методом GET или POST (префильтр new Bitrix\Main\Engine\ActionFilter\HttpMethod()) и если это POST запрос, то в запросе должна быть переменная sessid (префильтр new Bitrix\Main\Engine\ActionFilter\Csrf()). Это набор дефолтных префильтров. Если метод configureActions() ничего не будет возвращать, то именно эти префильтры по дефолту подставит битрикс. Если же метод возвращает массив, в одном из ключей которого есть описание метода test, то выполнятся именно пользовательские префильтры, а стандартные будут проигнорированы.

Пример класса с фильтрами:

/local/components/my_components/ajax/class.php<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
// основной класс, является оболочкой компонента унаследованного от CBitrixComponent
class CIblocList extends CBitrixComponent implements \Bitrix\Main\Engine\Contract\Controllerable
{
    // обязательный метод предпроверки данных
    public function configureActions()
    {
        // устанавливаем фильтры (Bitrix\Main\Engine\ActionFilter\Authentication() и Bitrix\Main\Engine\ActionFilter\HttpMethod() и Bitrix\Main\Engine\ActionFilter\Csrf())
        return [
            'test' => [
                'prefilters' => [
                    new Bitrix\Main\Engine\ActionFilter\Authentication(),
                    new Bitrix\Main\Engine\ActionFilter\HttpMethod(array(Bitrix\Main\Engine\ActionFilter\HttpMethod::METHOD_GET, Bitrix\Main\Engine\ActionFilter\HttpMethod::METHOD_POST)),
                    new Bitrix\Main\Engine\ActionFilter\Csrf(),
                ],
                'postfilters' => []
            ]
        ];
    }
    // основной метод исполнитель, сюда передаются параметры из ajax запроса, навания точно такие же как и при отправке запроса, $_REQUEST['param1'] будет передан в $param1
    public function testAction($param2 = 'qwe', $param1 = '')
    {
        return [
        'asd' => $param1,
        'count' => 200
        ];
    }
}

Пример класса со сброшенными фильтрами:

/local/components/my_components/ajax/class.php<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
// основной класс, является оболочкой компонента унаследованного от CBitrixComponent
class CIblocList extends CBitrixComponent implements \Bitrix\Main\Engine\Contract\Controllerable
{
    // обязательный метод предпроверки данных
    public function configureActions()
    {
        // сбрасываем фильтры по-умолчанию (Bitrix\Main\Engine\ActionFilter\Authentication() и Bitrix\Main\Engine\ActionFilter\HttpMethod() и Bitrix\Main\Engine\ActionFilter\Csrf()), предустановленные фильтры находятся в папке /bitrix/modules/main/lib/engine/actionfilter/
        return [
            'test' => [
                'prefilters' => [],
                'postfilters' => []
            ]
        ];
    }
    // основной метод исполнитель, сюда передаются параметры из ajax запроса, навания точно такие же как и при отправке запроса, $_REQUEST['param1'] будет передан в $param1
    public function testAction($param2 = 'qwe', $param1 = '')
    {
        return [
        'asd' => $param1,
        'count' => 200
        ];
    }
}

Запрос в компонент к файлу ajax.php

В примере выше в клиентском запросе мы указывали mode: 'class', если посмотреть jsdoc битриксовой функции, то оказывается, что она принимает еще и mode: 'ajax'. На самом деле это практически то же самое, только подключается не class.php из папка компонента, а файл ajax.php. Ну и сам класс нужно наследовать немного иначе.

Клиентская сторона

// делаем ajax запрос в компонент my_components:ajax к методу testAction()
var request = BX.ajax.runComponentAction('my_components:ajax', 'test', {
mode: 'ajax',
data: {
param1: 'asd',
sessid: BX.message('bitrix_sessid')
}
});
// промис в который прийдет ответ
request.then(function (response) {
console.log(response);
});

Серверная сторона

/local/components/my_components/ajax/ajax.php<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
// основной класс, является оболочкой компонента унаследованного от CBitrixComponent
class CustomAjaxController extends \Bitrix\Main\Engine\Controller
{
    // обязательный метод предпроверки данных
    public function configureActions()
    {
        // сбрасываем фильтры по-умолчанию (Bitrix\Main\Engine\ActionFilter\Authentication() и Bitrix\Main\Engine\ActionFilter\HttpMethod() и Bitrix\Main\Engine\ActionFilter\Csrf()), предустановленные фильтры находятся в папке /bitrix/modules/main/lib/engine/actionfilter/
        return [
            'test' => [
                'prefilters' => [],
                'postfilters' => []
            ]
        ];
    }
    // основной метод исполнитель, сюда передаются параметры из ajax запроса, навания точно такие же как и при отправке запроса, $_REQUEST['param1'] будет передан в $param1
    public function testAction()
    {
        return 'Тест пройден';
    }
}

Пример Битриксовой функции для обращения к модулю

Приведу код и jsdoc битриксовой функции. Из jsdoc можно узнать параметры, которые эта функция ожидает:

/**
*
* @param {string} action
* @param {Object} config
* @param {?string} [config.analyticsLabel]
* @param {string} [config.method='POST']
* @param {Object} [config.data]
* @param {?Object} [config.headers]
* @param {?Object} [config.timeout]
* @param {Object} [config.navigation]
* @param {number} [config.navigation.page]
*/
BX.ajax.runAction = function(action, config)
{
config = prepareAjaxConfig(config);
var getParameters = prepareAjaxGetParameters(config);
getParameters.action = action;
var url = BX.util.add_url_param('/bitrix/services/main/ajax.php', getParameters);
return buildAjaxPromiseToRestoreCsrf({
method: config.method,
dataType: 'json',
url: url,
data: config.data,
timeout: config.timeout,
preparePost: config.preparePost,
headers: config.headers
});
};

Запрос к модулю

Попробуем отправлять запрос не к компоненту а к модулю, будем отправлять запросы к модулю local.lib.

Клиентская сторона

Разберем тестовый модуль:

  • local.lib модуль
  • api неймспейс который мы зарегистрировали в файле .settings.php модуля
  • test название файла и класса в папке lib/controller
  • example метод exampleAction в классе Test, без суффикса action

Пример запроса на битриксовой библиотеке, при использовании BX.ajax.runComponentAction() сессия передается автоматически:

var request = BX.ajax.runAction('local:lib.api.test.example', {
data: {
param1: 'hhh'
}
});
request.then(function(response){
console.log(response);
});

Примерно то же самое на jquery:

var query = {
action: 'local:lib.api.test.example',
};
var data = {
param1: 'eee',
SITE_ID: 's1',
//sessid: BX.message('bitrix_sessid')
};
var request = $.ajax({
url: '/bitrix/services/main/ajax.php?' + $.param(query),
method: 'POST',
data: data
});
request.done(function (response) {
console.log(response);
});

Серверная сторона

На серверной стороне есть отличия. Для начала в папке с модулем нам понадобится файл .settings.php, именно в папке с модулем. В этом файле мы регистрируем целый неймспейс. Или другими словами регистрируем целую папку, вся папка будет содержать контроллеры для работы с аяксом:

/local/modules/local.lib/.settings.php<?php
return [
    'controllers' => [
        'value' => [
            'namespaces' => [
                '\\Local\\Lib\\Controller' => 'api'
            ]
        ],
        'readonly' => true
    ]
];

Далее создаем папку lib/controller и в этой папке файл test.php, содержимое тут примерно такое же как и в файле ajax.php компонента:

/local/modules/local.lib/lib/controller/test.php<?php
namespace Local\Lib\Controller;
use Bitrix\Main\Engine\Controller;
class Test extends Controller
{
    /**
     * @return array
     */
    public function configureActions()
    {
        return [
            'example' => [
                'prefilters' => []
            ]
        ];
    }
    /**
     * @param string $param2
     * @param string $param1
     * @return array
     */
    public static function exampleAction($param2 = 'qwe', $param1 = '')
    {
        return [
            'asd' => $param1,
            'count' => 300
        ];
    }
}

Метод configureActions()

Существует два типа фильтров:

  • prefilter выполняется до запуска Action, может отклонить выполнения действия
  • postfilter выполняется после запуска Action, может изменить результат выполнения действия

Предустановленные битриксом фильтры находятся в папке /bitrix/modules/main/lib/engine/actionfilter/.

Если в configureActions() оставить пустой массив prefilters, то метод-действие будет работать без фильтров.

Если не указывать ключ prefilters, то будут применены фильтры по умолчанию — ActionFilter\Authentication и ActionFilter\Csrf.

/local/components/my_components/ajax/class.phppublic function configureActions(): array
{
    return [
        'test' => [
            'prefilters' => [] // метод testAction() будет работать без фильтров
        ]
    ];
}
/local/components/my_components/ajax/class.phppublic function configureActions(): array
{
    return [
        'test' => []  // метод testAction() будет работать у авторизованных пользователей, которые передали CSRF-токен
    ];
}

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

/local/components/my_components/ajax/class.phppublic function configureActions()
{
    return [
        'test' => [
            'prefilters' => [
                new ActionFilter\Authentication,
                new ActionFilter\HttpMethod([
                    ActionFilter\HttpMethod::METHOD_PUT
                ])
            ],
        ],
    ];
}

можно убрать проверку csrf токена на бэкенде, это делается именно тут, в методе configureActions(). Чтобы csrf токен не проверялся нужно убрать ActionFilter\HttpMethod и ActionFilter\Csrf из возвращаемого массива для конкретного ajax метода, недостаточно убрать просто ActionFilter\Csrf, если в ActionFilter\HttpMethod разрешен POST, то префильтр ActionFilter\Csrf автоматически включается.

Фильтры - это обработчики, которые выполняются до или после Action. С их помощью можно отклонить выполнение действия, либо повлиять на результат действия.

\Bitrix\Main\Engine\ActionFilter\HttpMethod();
Метод Параметр и описание
__construct(
array $allowedMethods = [self::METHOD_GET]
)
Проверяет по какому http методу запускается действие и блокирует выполнение действия, если метод не был перечислен.
$allowedMethods {array}. Список допустимых http методов. По умолчанию GET
\Bitrix\Main\Engine\ActionFilter\Authentication();
Метод Параметр и описание
__construct(
$enableRedirect = false
)
Проверяет аутентифицирован ли пользователь и блокирует выполнение действия, если проверка не прошла, установив http status 401. Может выполнить редирект на страницу авторизации при необходимости.
$enableRedirect {bool}. Включает или отключает автоматический редирект на страницу авторизации, если проверка не прошла успешно. По умолчанию false.
\Bitrix\Main\Engine\ActionFilter\Csrf();
Метод Параметр и описание
__construct(
$enabled = true,
$tokenName = 'sessid',
$returnNew = true
)
Проверяет наличие и корректность csrf-токена и блокирует выполнение действия, если проверка не прошла.
  • $enabled {bool} Включает или отключает проверку на токен. По умолчанию true.
  • $tokenName {string} Имя, по которому передается значение токена. Используется для поиска в $_GET, $_POST. По умолчанию sessid.
  • $returnNew {bool} Сигнализирует нужно ли возвращать новое значение токена, если проверка прошла неуспешно. Заметим, что возврат осуществляется через \Bitrix\Main\Error::$customData. По умолчанию true.
\Bitrix\Main\Engine\ActionFilter\CloseSession();
Метод Параметр и описание
__construct(
$enabled = true
)
Выполнит session_write_close() перед выполнением действия. Будьте внимательны при использовании данного фильтра! Так как после закрытия сессии, все внесенные в неё изменения не будут сохранены. Для понимания работы сессий, стоит ознакомиться с документацией на php.net
$enabled {bool}. Включает или отключает работу фильтра. По умолчанию true
\Bitrix\Main\Engine\ActionFilter\Scope();
Метод Параметр и описание
__construct(
$scopes
)
Позволяет заблокировать определенные действия для указанного scope, например, можно открыть только определенные действия для rest, а все остальные будут доступны только для ajax.
$scopes {int} Перечисление допустимых scopes. Для проверки используются битовые маски. Доступные варианты можно найти в константах класса \Bitrix\Main\Engine\ActionFilter\Scope
\Bitrix\Main\Engine\ActionFilter\Cors();
Метод Параметр и описание
__construct(
string $origin = null,
bool $credentials = false
)
Устанавливает заголовки ответа для управления CORS.
  • $origin {string} Используется для установки заголовка Access-Control-Allow-Origin. По умолчанию null
  • $credentials {bool} Если true, то устанавливается заголовок Access-Control-Allow-Credentials. По умолчанию false
\Bitrix\Main\Engine\ActionFilter\ContentType();
Метод Параметр и описание
__construct(
array $allowedTypes
)
Фильтр разрешает выполнять действие, только в случае допустимых content-type в запросе. При работе с application/json будет автоматически зарегистрирован объект \Bitrix\Main\Engine\JsonPayload, который можно внедрять в параметры через параметры аякс-действия.
$allowedTypes {array}. Перечисление допустимых content-type. Например, ['application/json']
\Bitrix\Main\Engine\ActionFilter\PostDecode();
Параметр и описание
Фильтр перекодирует данные из POST-запроса, если внутренняя кодировка проекта отличается от utf-8

Отправка запроса напрямую

Если не хочется использовать BX.ajax.runComponentAction(), можно отправить запрос напрямую, например используя jQuery. Запросы отправляются на /bitrix/services/main/ajax.php, запрос будет выглядеть примерно так:

$.post(
"/bitrix/services/main/ajax.php?mode=class&c=my_components:ajax&action=test",
{
param1: 'asd',
sessid: BX.message('bitrix_sessid')
},
function (response) {
console.log(response);
}
);
  • mode с каким файлом работаем, class.php или ajax.php
  • c имя компонента в формате vendor:component
  • action запускаемый метод

Использование ЧПУ

В urlrewrite.php можно прописать красивый адрес, например /api/rest-component/<component_vendor>/<component_name>/<action>/.

Для этого добавим в urlrewrite.php следующий массив:

urlrewrite.php$arUrlRewrite = [
    [
        "CONDITION" => "#^/api/rest-component/([a-zA-Z0-9]+)/([a-zA-Z0-9\.]+)/([a-zA-Z0-9]+)/?.*#",
        "RULE" => "mode=class&c=$1:$2&action=$3",
        "PATH" => "/bitrix/services/main/ajax.php",
    ],
];

Теперь запрос будет выглядеть понятнее:

function sendFeedback(form) {
    const route = "/api/rest-component/machaon/feedback/send/";
    const data = $(form).serialize();
    $.post(route, data, function (response) {
        console.log(response);
    });
}
Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг