ГлавнаяБлогAJAX в 1С-Битрикс: актуальные способы вместо CPHttpRequest

AJAX в 1С-Битрикс: актуальные способы вместо CPHttpRequest

Рамиль Юналиев
Рамиль Юналиев
E-Commerce Lead
10 июля 2026 г.
7 мин чтения

В 2010 году на этом блоге был пост про AJAX в Битрикс с CPHttpRequest и jsAjaxUtil. С тех пор в платформе появился D7, а сам старый API остался — но для новых задач его выбирать не стоит. На стороне сервера актуальны три способа: через контроллер компонента, через контроллер модуля или через свой обработчик. На клиенте ко всем можно обращаться чем угодно — обычным fetch, jQuery или из Vue-компонента, бэкенд от этого не меняется.

Между CPHttpRequest и fetch в истории API был ещё промежуточный BX.ajax({ url, method, data, onsuccess, onfailure }) — низкоуровневая обёртка над XMLHttpRequest, без промисов и без автоматического CSRF. Она рабочая и встречается в старом коде, но в новых проектах проще сразу fetch или BX.ajax.runComponentAction — добавлять BX.ajax в новый код незачем.

Способ 1: BX.ajax.runComponentAction — если работа идёт через компонент

Штатный способ D7. Компонент реализует интерфейс Controllerable, действия (actions) описываются методом configureActions(), а вызов идёт через BX.ajax.runComponentAction — Битрикс сам подставляет CSRF-токен, а если тот истёк — получает новый и повторяет запрос.

class.php компонента

<?php
use Bitrix\Main\Engine\ActionFilter;
use Bitrix\Main\Engine\Contract\Controllerable;
 
class ExampleComponent extends \CBitrixComponent implements Controllerable
{
    public function configureActions(): array
    {
        return [
            'loadMore' => [
                'prefilters' => [
                    new ActionFilter\Csrf(),
                ],
            ],
        ];
    }
 
    public function loadMoreAction(int $page): array
    {
        return [
            'items' => $this->getItems($page),
        ];
    }
 
    public function executeComponent()
    {
        $this->includeComponentTemplate();
    }
}

JavaScript

BX.ajax.runComponentAction('vendor:example', 'loadMore', {
  mode: 'class',
  data: { page: 2 },
}).then(function (response) {
  console.log(response.data.items);
}, function (response) {
  console.error(response.errors);
});

response.data приходит уже разобранным JSON, ошибки — отдельным полем errors, промис завершается успешно (resolve) только при status: 'success', иначе — reject. Для действий модуля, не привязанных к компоненту, — способ 2 ниже.

Плюс подхода — CSRF, обработка ошибок и связь action → метод контроллера уже сделаны платформой. Минус — годится только если данные и так отдаёт компонент; для произвольного обработчика вне компонентной модели это лишний код.

Способ 2: BX.ajax.runAction — контроллер модуля, если компонента нет

Компонент под обработчик поднимать не всегда уместно, но платформенные CSRF и авторизация нужны — для этого в D7 есть контроллер модуля, отдельный от компонентных. Класс лежит в lib/Controller/ и наследует \Bitrix\Main\Engine\Controller.

.settings.php модуля

<?php
return [
    'controllers' => [
        'value' => [
            'defaultNamespace' => '\\Vendor\\Example\\Controller',
        ],
        'readonly' => true,
    ],
];

lib/Controller/Item.php

<?php
namespace Vendor\Example\Controller;
 
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\ActionFilter;
use Bitrix\Main\Error;
 
class Item extends Controller
{
    protected function getDefaultPreFilters(): array
    {
        return [
            new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]),
            new ActionFilter\Csrf(),
        ];
    }
 
    public function addAction(array $fields): ?array
    {
        $id = $this->saveItem($fields);
 
        if (!$id) {
            $this->addError(new Error('Не удалось сохранить запись', 'ITEM_SAVE_ERROR'));
            return null;
        }
 
        return ['id' => $id];
    }
}

Метод должен быть публичным и оканчиваться на Action — остальное имя становится именем action. Параметры (array $fields выше) платформа сама достаёт из запроса и приводит к нужному типу — так же, как в configureActions() компонента. addError() + return null дают тот же формат ошибки, что и у компонента: {"status": "error", "errors": [...]}; обычный return массива или объекта, реализующего Arrayable/JsonSerializable, — {"status": "success", "data": ...}.

По умолчанию getDefaultPreFilters() (без переопределения) уже включает Authentication, HttpMethod(GET, POST) и Csrf — то есть неавторизованный пользователь получит 401 без единой строчки кода. Для публичного действия (форма для гостя, счётчик) Authentication из фильтров нужно убрать явно, как в примере выше — иначе анонимный запрос отвалится ещё до вызова метода.

JavaScript

BX.ajax.runAction('vendor:example.Item.add', {
  data: { fields: { NAME: 'test' } },
}).then(function (response) {
  console.log(response.data.id);
}, function (response) {
  console.error(response.errors);
});

Формат action — вендор:модуль.ИмяКлассаКонтроллера.имяМетодаБезAction. CSRF-токен BX.ajax.runAction подставляет и обновляет сам — так же, как runComponentAction в способе 1.

Способ 3: свой обработчик на fetch — если и контроллер не нужен

Контроллер модуля предполагает саму структуру модуля (lib/, .settings.php) — для одноразового служебного скрипта вне модуля это лишнее. Тогда актуальная версия того, что раньше делал CPHttpRequest, — обычный fetch к своему PHP-файлу с CSRF и разбором ответа руками.

Серверный обработчик

<?php
define('STOP_STATISTICS', true);
require($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/prolog_before.php');
 
if (!check_bitrix_sessid()) {
    (new \Bitrix\Main\Engine\Response\Json(['error' => 'bad sessid'], JSON_UNESCAPED_UNICODE))->send();
    require($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/epilog_after.php');
    exit;
}
 
$param = htmlspecialchars($_POST['param'] ?? '', ENT_QUOTES, 'UTF-8');
 
(new \Bitrix\Main\Engine\Response\Json(['param' => $param], JSON_UNESCAPED_UNICODE))->send();
require($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/epilog_after.php');

STOP_STATISTICS по-прежнему исключает запрос из счётчика статистики Битрикс — из констант 2010 года она не единственная, что дожила до наших дней, но самая частая в AJAX-обработчиках. \Bitrix\Main\Engine\Response\Json сам ставит заголовок Content-Type: application/json; charset=UTF-8 — кодировка windows-1251 из старого поста для новых проектов не нужна, D7 работает в UTF-8. После send() важно ничего больше не выводить — epilog_after.php в конце обязателен (закрывает сессию, финализирует буфер), а не echo/var_dump для отладки.

JavaScript

fetch('/ajax/example.php', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    param: 'test_param',
    sessid: BX.bitrix_sessid(),
  }),
})
  .then((r) => {
    if (!r.ok) {
      throw new Error(`HTTP ${r.status}`);
    }
    return r.json();
  })
  .then((data) => {
    document.getElementById('container').innerHTML = data.param;
  });

fetch не отклоняет промис на HTTP-ошибках (404, 500) — только на сетевой сбой, поэтому r.ok нужно проверять руками, иначе .json() на пустом теле ответа падает с непонятной ошибкой. BX.bitrix_sessid() отдаёт текущий CSRF-токен на клиенте — без него check_bitrix_sessid() на сервере вернёт false. Индикатор загрузки делаем своей вёрсткой — jsAjaxUtil.ShowLocalWaitWindow() из старого поста тоже устарел вместе с остальным API.

Для загрузки файла тем же способом вместо URLSearchParamsFormData, Content-Type при этом не указываем, браузер сам подставит правильный boundary:

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('sessid', BX.bitrix_sessid());
 
fetch('/ajax/example.php', {
  method: 'POST',
  body: formData,
}).then((r) => r.json());

Тот же $_POST['sessid'] и $_FILES['file'] на сервере — check_bitrix_sessid() из примера выше работает без изменений, файл читается штатным $_FILES. BX.ajax.runComponentAction тоже принимает FormData в data — для action-метода компонента ничего дополнительно готовить не нужно.

jQuery — если библиотека уже в проекте

Для старых проектов, где jQuery уже подключён, $.ajax — прямая замена CPHttpRequest/fetch из способа 3. Обращается к тому же обработчику, серверная часть не меняется:

$.ajax({
  url: '/ajax/example.php',
  type: 'POST',
  data: {
    param: 'test_param',
    sessid: BX.bitrix_sessid(),
  },
  success: function (data) {
    $('#container').html(data.param);
  },
  error: function (xhr) {
    console.error(xhr.responseText);
  },
});

Разница с fetch — только синтаксис. Для BX.ajax.runComponentAction (способ 1) jQuery не нужен вообще — это уже готовый промис, оборачивать его в $.ajax незачем.

Vue-компонент — та же логика, реактивный клиент

Если фронтенд собирается на Vue (виджет, форма, личный кабинет) внутри шаблона компонента Bitrix, AJAX-логика та же самая — просто вызывается из метода компонента, а не напрямую из обработчика клика.

Composition API (актуальный вариант, Vue 3)

<script setup>
import { ref, onMounted } from 'vue';
 
const items = ref([]);
const loading = ref(false);
 
async function loadMore(page) {
  loading.value = true;
  const response = await BX.ajax.runComponentAction('vendor:example', 'loadMore', {
    mode: 'class',
    data: { page },
  });
  items.value.push(...response.data.items);
  loading.value = false;
}
 
onMounted(() => loadMore(1));
</script>
 
<template>
  <ul>
    <li v-for="item in items" :key="item.id">{{ item.title }}</li>
  </ul>
  <button :disabled="loading" @click="loadMore(2)">Ещё</button>
</template>

BX.ajax.runComponentAction возвращает промис — в async-методе Vue его достаточно await-нуть. Для этих двух примеров axios не требуется — в большом SPA со своим API-слоем он всё ещё может быть оправдан, но конкретно для контроллера компонента или обработчика на fetch это лишняя зависимость.

Options API (старый код на Vue 2 или ранний Vue 3)

Та же логика без <script setup>:

export default {
  data() {
    return { items: [], loading: false };
  },
  methods: {
    async loadMore(page) {
      this.loading = true;
      const response = await BX.ajax.runComponentAction('vendor:example', 'loadMore', {
        mode: 'class',
        data: { page },
      });
      this.items.push(...response.data.items);
      this.loading = false;
    },
  },
  mounted() {
    this.loadMore(1);
  },
};

Что выбрать

  • Данные и так приходят из компонента (каталог, список, фильтр) — BX.ajax.runComponentAction, CSRF и разбор ответа не пишутся руками.
  • Нужен отдельный обработчик без привязки к компоненту, но с платформенными CSRF/авторизацией — контроллер модуля (BX.ajax.runAction), getDefaultPreFilters() под задачу.
  • Разовый служебный скрипт вне модульной структуры — свой на fetch, CSRF через check_bitrix_sessid() / BX.bitrix_sessid() проверяется вручную.
  • jQuery уже в проекте — $.ajax вместо fetch для своего обработчика; для runComponentAction/runAction без разницы, оба и так возвращают промис.
  • Фронтенд на Vue — тот же BX.ajax.runComponentAction/fetch, просто вызванный из метода компонента (Composition API — актуальный вариант, Options API — для старого кода).
  • CPHttpRequest и jsAjaxUtil — только если поддерживается старый проект, где они уже есть; в новый код их добавлять незачем.

Независимо от способа: если запрос меняет данные — метод POST, GET оставляем только для чтения.

Если AJAX-запрос не срабатывает и непонятно почему — отдельный старый пост про отладку AJAX в админке Битрикс.