AJAX-запрос - контроллер, свой файл и ответ в JSON
Принимаем асинхронный запрос на стороне сайта и возвращаем ответ, который браузер сможет разобрать.
Решение
Принимаем запрос контроллером:
namespace Local\Controller;
use Bitrix\Main\Engine\Controller;
class Feedback extends Controller{ public function saveAction(string $name, string $phone): array { // параметры приходят типизированными аргументами, разбирать их не нужно return ['status' => 'ok', 'name' => $name]; // ответ уедет в формате JSON // исключение внутри действия превратится в ответ с описанием ошибки }
public function configureActions(): array { // здесь снимают проверку подписи или добавляют свои правила доступа return ['save' => ['prefilters' => []]]; // пустой список снимает штатные проверки }}Контроллер сам разбирает параметры, проверяет подпись запроса и упаковывает ответ. Возвращённый массив уходит в браузер как готовый JSON, печатать его самому не нужно.
Зовём контроллер со стороны браузера:
BX.ajax.runAction('local:feedback.feedback.save', { data: { name: 'Иван', phone: '+7 900 000-00-00' },}).then((response) => { console.log(response.data); // здесь лежит то, что вернул метод});Имя действия собирается из пространства имён, класса и метода. Ошибка в нём даёт отказ ещё до выполнения кода, и в ответе приходит описание с именем ненайденного действия.
Обходимся своим файлом, когда контроллер избыточен:
define('NO_KEEP_STATISTIC', true);define('NOT_CHECK_PERMISSIONS', true);require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
header('Content-Type: application/json; charset=UTF-8');echo json_encode(['count' => 42], JSON_UNESCAPED_UNICODE);require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php';Облегчённое подключение даёт доступ к базе и к API без сборки страницы. Полный пролог публичной части здесь лишний: он собирает шаблон сайта, которого в ответе всё равно не будет.
Включаем режим обмена у компонента:
'AJAX_MODE' => 'Y','AJAX_OPTION_HISTORY' => 'N','AJAX_OPTION_JUMP' => 'N', // не прокручивать страницу к компонентуВ этом режиме компонент отдаёт только свою часть страницы, а не документ целиком. Запрос при этом остаётся обычным обращением к той же странице, и кэширование работает как всегда.
Чистота ответа важнее его содержимого. Предупреждение PHP, пробел перед открывающим тегом в подключённом файле или забытая отладочная печать попадают в ответ раньше данных и ломают его разбор. Поэтому на боевом сервере вывод ошибок в ответ выключают, а отладочные сообщения пишут в файл.
Выбор точки входа определяется ценой запроса. Контроллер уместен там, где есть логика, права и структурированный ответ; свой файл - там, где нужен один счётчик или короткая проверка; режим обмена у компонента - там, где обновляется готовый кусок страницы. Смешивать эти способы в одном месте кода не стоит: через полгода никто не вспомнит, почему один вызов ушёл в контроллер, а соседний в файл.
Точка обмена доступна каждому, кто знает её адрес. Проверку прав в ней пишут явно: контроллер это умеет через свои правила, а свой файл - только руками. Отключение проверки прав ради удобства открывает данные всему интернету.
Типичные проблемы
В ответ приходит вся страница сайта.
Запрос ушёл на обычную страницу, а не в точку обмена. Компонент в режиме обмена и контроллер отдают только свою часть.
Ответ не разбирается, хотя данные верные.
В него попал посторонний вывод: предупреждение PHP, пробел или отладочная печать. Ответ должен содержать только данные.
Контроллер отвечает «действие не найдено».
Ошибка в имени действия или файл класса лежит не там, где его ищет автозагрузка.
В обработчике недоступны данные пользователя.
Ядро не подключено или подключено слишком облегчённо. Без пролога платформа не знает ни о сессии, ни о правах.
Запрос выполняется секунду вместо десятков миллисекунд.
Подключён полный пролог публичной части. Он собирает шаблон сайта, который в ответе не нужен.
Частые вопросы
Как отправить файл асинхронным запросом?
Обычной формой с данными в двоичном виде: контроллер получит файл в аргументе. Кодировать его в текст не нужно, и на больших файлах это только вредит.
Почему результат запроса не кэшируется?
Кэш компонента работает и в режиме обмена, но свой файл-обработчик не кэшируется ничем. Кэширование в нём пишут руками через кэш данных.
Можно ли обращаться к файлу инициализации напрямую?
Нет, это файл подключения, а не точка входа. Свой обработчик кладут отдельным файлом и подключают в нём ядро.
Как вернуть ошибку из контроллера?
Добавлением ошибки в коллекцию ошибок контроллера: она приходит в ответе отдельным полем. Печатать текст ошибки вручную не нужно.
Смежное
- Подгрузка списка порциями: кнопка «Показать ещё» и прокрутка - частый случай запроса за разметкой
- AJAX-запросы - оглавление подтемы
- Проверка входных данных атрибутами: правила, результат, контроллер - проверка данных до кода действия
- Не работает AJAX-запрос: разбор причин - когда запрос не доезжает или не разбирается
- JS-ядро и интерфейсы - что доступно на стороне браузера
- AJAX-действия своего компонента: действия, параметры, ошибки - запрос прямо в компонент, без контроллера
- Компоненты 2.0 - режим обмена и кэш компонента
- Сборка фронтенда рядом с шаблоном: исходники, бандл, приложение - приложение на фреймворке рядом с шаблоном
- Ядро D7 - основы - контроллеры и подключение ядра
- Форма обратной связи: веб-форма, письма, свой обработчик - готовый механизм вместо своего кода
- Отправка формы через AJAX: проверка полей, защита, ответ с ошибками - готовый разбор формы на том же механизме
- Сообщения на страницу без перезагрузки: отправка, подписка, каналы - обновление страницы без опроса сервера
- Своё расширение JS: каталог, зависимости, подключение - куда положить клиентский код
- Своё API для приложения: контроллер, токен, версии - те же контроллеры для внешних клиентов
- Окна и уведомления ядра UI: подтверждение, окно, сообщение - как показать ответ сервера
- Контроллер изнутри: маршрут, действие, фильтры, ответ - что платформа делает с запросом до действия