Отдельные PHP-файлы «для ajax», самописный роутинг и echo json_encode без CSRF — частый путь к дырявым интеграциям и поломке композита. В Битрикс есть штатные варианты: параметр AJAX_MODE у компонента, ручная ветка isAjaxRequest() и BX.ajax.runComponentAction на Engine. Ниже — когда какой способ брать и как не сломать кеш.
Связанные темы: Web\Json, Page\Asset, Application / Context / Request, Engine\Controller: AJAX, runAction и типичные ошибки, REST API в D7.
Способ 1: штатный AJAX_MODE
Для пагинации, сортировки и простых фильтров достаточно включить режим у компонента — ядро перерисует блок без полной перезагрузки:
$APPLICATION->IncludeComponent(
'company:catalog.list',
'',
[
'AJAX_MODE' => 'Y',
'AJAX_OPTION_JUMP' => 'N',
'AJAX_OPTION_STYLE' => 'Y',
'AJAX_OPTION_HISTORY' => 'N',
]
);
AJAX_OPTION_STYLE=Y просит подтянуть CSS/JS фрагмента. Имеет смысл заранее зарегистрировать стили через Asset на полной отрисовке, а не надеяться, что всё приедет только с ajax-ответом.
Способ 2: ручной AJAX в компоненте
Когда нужен JSON (load more, избранное, пересчёт корзины) — обрабатывайте запрос в том же компоненте:
// class.php / component.php
use Bitrix\Main\Web\Json;
if ($this->request->isAjaxRequest()) {
$action = (string)$this->request->get('action');
if ($action === 'load_more') {
if (!check_bitrix_sessid()) {
$this->sendJson(['error' => 'invalid_sessid'], 403);
}
$page = max(1, (int)$this->request->get('page'));
$items = $this->loadItems($page);
$this->sendJson([
'items' => $items,
'hasMore' => count($items) === (int)$this->arParams['PAGE_SIZE'],
]);
}
}
protected function sendJson(array $data, int $status = 200): void
{
$response = \Bitrix\Main\Context::getCurrent()->getResponse();
$response->setStatus($status);
$response->addHeader('Content-Type', 'application/json; charset=UTF-8');
$response->flush(Json::encode($data));
die();
}
Не используйте «голый» json_encode без проверки ошибок — берите Bitrix\Main\Web\Json. Не отдавайте HTML ошибки PHP внутри JSON: иначе фронт получит невалидный ответ.
JavaScript: fetch + sessid
async function loadMore(page) {
const body = new URLSearchParams({
action: 'load_more',
page: String(page),
sessid: BX.message('bitrix_sessid'),
});
const response = await fetch(window.location.pathname + window.location.search, {
method: 'POST',
headers: {
'X-Bitrix-Csrf-Token': BX.message('bitrix_sessid'),
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
},
body,
credentials: 'same-origin',
});
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
const data = await response.json();
renderItems(data.items);
}
Способ 3: BX.ajax.runComponentAction
Для контроллеров Engine у компонента — штатный JS-хелпер. CSRF и формат ответа закрывает ядро:
BX.ajax.runComponentAction(
'company:catalog.list',
'loadMore',
{
mode: 'class',
data: { page: 2 },
}
).then(function (response) {
console.log(response.data);
}).catch(function (err) {
console.error(err.errors || err);
});
В PHP action-метод должен заканчиваться на Action, prefilters — Authentication/Csrf/HttpMethod по необходимости. Каркас контроллеров и маршрутов — в гайде по REST API D7.
Если логика не привязана к конкретному компоненту (сохранение сущности модуля, общий виджет, админский ajax), современнее вынести её в модульный Bitrix\Main\Engine\Controller и вызывать через BX.ajax.runAction — см. Engine\Controller: AJAX, runAction и типичные ошибки. runComponentAction оставляйте для действий, которым нужен контекст компонента (шаблон, параметры, кеш компонента).
Защита и типичные ошибки
- Всегда проверяйте
check_bitrix_sessid()(или prefilter Csrf) на изменяющих действиях. - Не открывайте публичные
/ajax/*.phpбез проверки прав и sessid — это отдельная поверхность атаки. - Не кешируйте персональный JSON в файловом кеше компонента без учёта пользователя.
- Регистрируйте CSS/JS через Asset на полном хите; ajax только меняет разметку/данные.
- Ответ — через
Json::encodeи явныйContent-Type: application/json.
Вывод
Для пагинации и фильтров начинайте с AJAX_MODE. Для JSON-контракта компонента — ветка isAjaxRequest() или runComponentAction. Для модульного AJAX без привязки к компоненту — Engine\Controller: AJAX, runAction и типичные ошибки и BX.ajax.runAction. Отдельные «левые» PHP-эндпоинты без Engine оставляйте только для легаси. Сериализацию держите на Web\Json, ресурсы фронта — на Page\Asset.

