Действия своего модуля по REST - включение и доступ
Свой модуль умеет считать и отдавать данные, и теперь их просит партнёрская система. Открываем её действия наружу штатной витриной, а служебные методы оставляем закрытыми.
Решение
Чем отдать данные наружу
Сравниваем три штатных механизма отдачи данных:
SOAP-веб-сервис - легаси: класс IWebService, WSDL по ?wsdl, авторизация HTTP BasicREST-интеграция - действия контроллеров модуля открываются по REST, нужен rest 18.5.1+Модуль «REST API» - инфраструктура для локальных приложений и решений МаркетплейсДля нового кода берём REST-интеграцию контроллеров: действие уже написано, свой протокол и ручной разбор запроса не нужны. SOAP остаётся старым интеграциям на стороне заказчика, переписывать которые обычно уже некому.
Включаем витрину модуля
Ставим признак REST-интеграции в настройках модуля:
return [ 'controllers' => [ 'value' => [ 'defaultNamespace' => '\\Vendor\\Shop\\Controller', 'restIntegration' => ['enabled' => true], // нужен модуль rest 18.5.1 и выше ], 'readonly' => true, ],];Один флаг открывает по REST все действия всех контроллеров модуля, а не только те, что мы задумали отдать наружу. Поэтому включение витрины и ограничение доступа делаем одним заходом, а не двумя разными задачами.
Закрываем лишние действия
Запрещаем вызов снаружи для всего контроллера:
use Bitrix\Main\Engine;
protected function getDefaultPreFilters(){ return [ ...parent::getDefaultPreFilters(), // распаковываем CSRF, метод запроса, аутентификацию new Engine\ActionFilter\Scope(Engine\ActionFilter\Scope::NOT_REST), ];}Распаковка родительского списка обязательна: пересобранный с нуля набор снимает
штатную защиту действия. Фильтр Scope с признаком NOT_REST отклоняет вызов,
пришедший по REST, и не трогает обычные обращения со своих страниц.
Закрываем одно действие, оставляя соседние открытыми:
public function configureActions(){ return ['clearCache' => ['prefilters' => [ new Engine\ActionFilter\Scope(Engine\ActionFilter\Scope::NOT_REST), ]]];}Список в configureActions заменяет префильтры названного действия целиком.
Остальные действия контроллера остаются на общем наборе и по-прежнему видны
внешнему клиенту.
Достаём REST-контекст
Принимаем контекст вызова необязательным параметром:
public function getCatalogAction(int $sectionId, \CRestServer $restServer = null): array{ $clientId = $restServer ? $restServer->getClientId() : null; // кто именно зовёт return ['items' => $this->loadItems($sectionId, $clientId)];}Необязательным параметр делаем ради совместимости: то же действие зовут из
браузера обычным асинхронным запросом, и объекта CRestServer в нём нет.
Обязательный параметр сломает этот путь ещё на разборе аргументов, до первой
строки нашего кода.
Проверяем вызов и разбираем ответ
Зовём метод ключом и смотрим тело ответа:
curl -s "https://example.com/rest/1/КЛЮЧ/$METHOD.json" | head -20# успех: {"result": ..., "time": {"start": ..., "duration": ..., "processing": ...}}# отказ: {"error": "NO_AUTH_FOUND", "error_description": "..."}Успешный ответ приходит в поле result, рядом лежит служебное time с временами
обработки. Отказ выглядит иначе: только error и error_description, поля
result в нём нет вовсе.
Права на сами данные проверяет код действия, а не витрина REST. Витрина отвечает за авторизацию вызова,
а решение «этому клиенту эти строки» принимает наш код, и берёт он текущего
пользователя через CurrentUser, а не из глобальной переменной.
Типичные проблемы
Наружу открылось больше, чем планировали.
Признак restIntegration.enabled открывает по REST все действия всех контроллеров модуля. Каждое служебное действие закрывают префильтром отдельно, иначе наружу уходит и оно.
Обращение к /rest/ отвечает NO_AUTH_FOUND.
Вызов пришёл без данных авторизации, и до выполнения действия дело не дошло. Ключ доступа передают в самом адресе вызова, а не в теле или заголовке запроса.
В ответе приходит INTERNAL_WRONG_HANDLER_CLASS.
Класс обработчика не найден по описанию метода: неверное пространство имён в настройках модуля или опечатка в имени класса.
После правки фильтров действие перестало работать из браузера.
Метод getDefaultPreFilters переопределён своим списком, без распаковки родительского набора. Вместе с ним ушли штатные проверки CSRF, метода запроса и аутентификации.
Асинхронный вызов падает на разборе аргументов.
У параметра \CRestServer не проставлено значение по умолчанию, поэтому он считается обязательным. При обращении из браузера значения для него нет, и действие не выполняется.
Частые вопросы
Как зарегистрировать свой метод REST?
Отдельной регистрации метода не требуется. Достаточно поставить признак restIntegration.enabled в настройках модуля: действия его контроллеров становятся доступны по REST. Нужен модуль rest версии 18.5.1 и выше.
Что за поле time в ответе REST-метода?
Служебные времена обработки запроса: начало, окончание и длительность. Оно приходит рядом с result при успешном вызове. В ответе с ошибкой ни result, ни time нет.
Можно ли закрыть одно действие, не выключая REST целиком?
Да, префильтром Scope с признаком NOT_REST у нужного действия. Список префильтров действия задают через configureActions, а общий для контроллера - через getDefaultPreFilters.
Чем это отличается от своего API на контроллере?
Здесь работает штатная витрина REST со своей авторизацией и общим форматом ответа. Своё API - это отдельный протокол поверх точки входа, где формат ответа, токены и версии придумываем сами.
Как понять, что действие действительно закрыто?
Позвать его ключом и посмотреть ответ. Закрытое действие отвечает отказом, а не данными, при этом из браузера оно продолжает работать как прежде.
Смежное
- REST и вебхуки - оглавление подтемы
- Обмен с 1С и HTTP - устройство интеграций целиком
- Контроллер изнутри: маршрут, действие, фильтры, ответ - что делают префильтры до действия
- Своё API для приложения: контроллер, токен, версии - свой протокол вместо штатной витрины
- Вебхуки и вызовы REST: настройка, права, разбор ошибок - чем звать метод снаружи
- Свой модуль: структура, установка, настройки - где лежит файл настроек модуля
- Проверка входных данных атрибутами: правила, результат, контроллер - что делать с чужими аргументами
- Вызов внешнего сервиса из кода: таймауты, повторы, журнал - обратное направление обмена
- Мониторинг интеграций: что проверять, пороги, оповещение - следить за потоком вызовов
- Веб-сервис SOAP на сайте - тот самый легаси из сравнения трёх способов