Отправка формы через AJAX - проверка полей, защита, ответ с ошибками
Принимаем форму запросом без перезагрузки страницы: проверяем данные на сервере, закрываемся от ботов и отвечаем так, чтобы браузер показал ошибки по полям.
Решение
Отправляем данные вместе с идентификатором сеанса:
const data = new FormData(form);data.append('sessid', BX.bitrix_sessid()); // без него запрос отклонятconst res = await fetch('/local/ajax/feedback.php', { method: 'POST', body: data });const answer = await res.json();Идентификатор сеанса подставляется в каждый пишущий запрос к сайту. Он и отличает форму, отправленную с вашей страницы, от запроса, собранного кем-то со стороны, а проверяет его сервер, а не браузер.
Принимаем и проверяем на сервере:
define('NO_KEEP_STATISTIC', true);require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
$errors = [];if (!check_bitrix_sessid()) { $errors['form'] = 'Обновите страницу'; }if (!filter_var($_POST['EMAIL'] ?? '', FILTER_VALIDATE_EMAIL)) { $errors['EMAIL'] = 'Неверная почта'; }if (mb_strlen(trim($_POST['NAME'] ?? '')) < 2) { $errors['NAME'] = 'Укажите имя'; }Проверка в браузере нужна для удобства гостя, а не для надёжности. Запрос к обработчику отправляется чем угодно, поэтому серверная проверка обязательна для каждого поля, включая скрытые.
Проверяем картинку с кодом:
if (!$APPLICATION->CaptchaCheckCode($_POST['captcha_word'] ?? '', $_POST['captcha_sid'] ?? '')) { $errors['captcha'] = 'Код с картинки не совпал';}// код одноразовый: после проверки нужен новый идентификатор картинкиКод с картинки одноразовый: после проверки прежний идентификатор уже недействителен. Форма, оставляющая старую картинку после ошибки, обрекает посетителя на вторую неудачную попытку.
Отвечаем списком ошибок по полям:
header('Content-Type: application/json; charset=UTF-8');if ($errors) { echo Json::encode(['ok' => false, 'errors' => $errors]);} else { CEvent::Send('FEEDBACK_FORM', SITE_ID, ['NAME' => $_POST['NAME'], 'EMAIL' => $_POST['EMAIL']]); echo Json::encode(['ok' => true]);}require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php';Ответ со списком полей позволяет подсветить в форме именно то, что заполнено неверно. Одна строка «проверьте данные» заставляет посетителя искать ошибку самому, и на длинной форме это прямой путь к брошенной заявке.
Обработчик стоит держать отдельным файлом, а не куском шаблона страницы. Файл переживает смену шаблона, отдаёт чистый ответ без разметки и проверяется запросом из консоли за одну команду.
Заявку стоит сохранять в базу до отправки письма, а не вместо неё. Почта может не уйти по причинам, не зависящим от сайта, и запись в базе остаётся единственным следом обращения.
Обработчик формы доступен всем подряд и работает без авторизации. Ограничение числа отправок с одного адреса и проверка полей на длину - не паранойя, а условие, без которого форму находят рассыльщики за пару дней.
Типичные проблемы
Запрос отклоняется с ошибкой сеанса.
В данные формы не добавлен идентификатор текущего сеанса. Пишущие запросы без него платформа просто не принимает.
В ответе приходит разметка вместо данных.
В обработчике подключён полный пролог вместе с шаблоном сайта. Для ответа данными берут облегчённое подключение ядра.
Форма проходит с пустыми полями.
Проверка полей сделана только на стороне браузера. Запрос к обработчику отправляется в обход страницы и её кода.
Код с картинки не совпадает со второй попытки.
После проверки идентификатор картинки становится недействительным. Форма обязана получить новую картинку сразу после ошибки.
Заявки приходят пустыми пачками.
Обработчик открыт всем и не ограничивает частоту отправки. Рассыльщики находят такие адреса за считанные дни.
Частые вопросы
Чем свой файл лучше контроллера платформы?
Ничем: контроллер даёт готовую проверку сеанса и единый формат ответа. Свой файл берут, когда нужен полный контроль над подключением.
Как показать ошибки рядом с полями?
Ответом с картой «поле - текст ошибки». Браузеру остаётся разложить её по полям формы.
Нужна ли картинка с кодом, если есть скрытое поле-ловушка?
Ловушка отсеивает простых ботов, картинка - настойчивых. На форме с деньгами лучше оба способа.
Куда сохранять заявки?
В веб-форму модуля или в свой инфоблок. Хранить только письмом - значит потерять заявку вместе с почтой.
Смежное
-
AJAX на практике - оглавление подтемы
-
Проверка входных данных атрибутами: правила, результат, контроллер - штатные правила вместо своих условий
-
Согласие на обработку данных: соглашение, компонент, запись факта - согласие в форме без перезагрузки
-
Не работает AJAX-запрос: разбор причин - разбор отказов и пустых ответов
-
Заявка с формы не сохранилась: разбор причин - если запрос прошёл, а заявки нет
-
Спам через формы сайта: капча, скрытое поле, ограничение частоты - защита той же формы от ботов
-
AJAX-запрос: контроллер, свой файл и ответ в JSON - куда отправлять запрос
-
Форма обратной связи: веб-форма, письма, свой обработчик - готовый механизм форм
-
Письма с сайта не доходят: разбор причин - когда письмо с заявкой не пришло
-
Клиентская часть - устройство клиентской части целиком
-
Стили и скрипты в шаблоне: подключение, порядок, кэш - где подключают скрипт формы
-
Данные от посетителя: экранирование, проверка, сеанс - проверки на стороне сервера
-
Заявки с сайта: хранение, дубли, передача в CRM - куда складывать отправленное
-
Загрузка файла с формы: приём, проверки, прогресс, хранение - когда в форме есть файл