Bitrix24

Отладка приложения

Журнал моста, состояние токенов и разбор типичных ошибок: пустой фрейм, expired_token, insufficient_scope, ERROR_PLACEMENT_NOT_FOUND.

На этой странице · 11

Боевой Bitrix24 обмен BX24.js с порталом не показывает нигде: разработчик видит только последствия — «окно не изменило размер», «диалог не открылся». В демонстрационном портале виден сам кадр, и это главная отладочная ценность экрана.

Журнал моста

Кнопка «Журнал моста» в шапке демонстрационного портала открывает ленту кадров. Каждая строка — один кадр: направление (in — от приложения, out — от портала), APP_SID, команда или тип кадра, короткая сводка параметров. Строка раскрывается в полный JSON. Лента ограничена 200 последними кадрами: приложение, вызывающее resizeWindow в цикле, способно выдать тысячи кадров в минуту.

Что видно в журнале:

  • POST — отправка формы во фрейм со всеми полями, включая PLACEMENT_OPTIONS именно в том виде, в каком его получило приложение;
  • hello — библиотека загрузилась и просит окружение;
  • init — ответ портала: токены, домен, язык, признак первого запуска, точка встраивания и её js-интерфейс;
  • каждая команда BX24.*, ушедшая через мост, и результат по ней. Ошибка красится отдельно.

Команда, которой портал не знает, возвращается кадром с кодом UNKNOWN_COMMAND и текстом «Портал не знает команду «…»».

Приложение не вышло на связь

Если за 8 секунд от фрейма не пришло ни одного кадра, портал показывает подсказку. Порядок проверки в ней тот же, что стоит держать в голове:

  1. 1

    Обработчик отвечает на POST

    Портал открывает приложение POST-запросом, а не GET. Обработчик, который умеет только GET, отдаст 404 или 405, и во фрейме будет пусто.

  2. 2

    Страница подключает библиотеку и вызывает BX24.init()

    Адрес библиотеки — <адрес стенда>/api/v1/, он же показан в панели «Портал песочницы» на экране «Локальные приложения».

  3. 3

    Приложение не запрещает фрейминг

    X-Frame-Options: SAMEORIGIN или CSP без нужного frame-ancestors — и браузер не покажет страницу. Портал об этом не узнает: проверку делает браузер, и в бою будет ровно то же самое.

Если приложение открыто напрямую, вне фрейма, библиотека пишет в консоль предупреждение: методы, которым нужен портал, вернут ошибку, а getAuth(), getDomain(), getLang() и isAdmin() отдадут пустые значения. Отдельное предупреждение — на потерянный APP_SID: связаться с порталом нечем.

Состояние приложения

В карточке приложения видно то, чего в бою не видно нигде:

  • Токены — последние выданные пары целиком, с обратным отсчётом и состоянием «Действует», «Истёк» или «Отозван». Копируются кнопкой: тот же токен подставляется в curl.
  • Виджеты и Подписки на события — что приложение успело зарегистрировать через placement.bind и event.bind.
  • Открытия во фрейме — последние сессии с их APP_SID, точкой встраивания и отметкой, была ли это установка.

Запросы приложения попадают в общий журнал на экране «Логи запросов». Ответы, которые сформировал портал, помечены заголовком X-APIStend-Source: app-context и X-APIStend-App с client_id; их тело сохраняется в журнале целиком, потому что из спецификации оно не восстанавливается.

Типичные ошибки

expired_token на каждый вызов

Токен просрочен или замещён обновлением. Правильная реакция — обменять refresh_token на новую пару и повторить вызов; обновлять по расписанию нельзя.

В браузере это делает сама библиотека: получив expired_token, BX24.js просит у портала свежий токен через мост и повторяет вызов ровно один раз. Второй такой ответ уходит приложению как есть. client_secret в браузер при этом не попадает и попадать не должен.

Проверяется коротким сроком токена в карточке приложения (от 5 секунд) или кнопкой «Состарить сейчас».

NO_AUTH_FOUND вместо expired_token

Это другая ошибка: токена не существует, он отозван или приложение удалено. Обновлять нечего — нужно пройти цикл OAuth заново. Токены отзываются при удалении приложения и при сбросе установки.

insufficient_scope

У приложения нет права на этот метод. Права определяются по префиксу: crm.* требует crm, task.* — task. Проверить, не вызывая метод, можно через method.get — он вернёт isAvailable: false.

Добавленное в карточке право действует сразу, в том числе для уже выданных токенов: проверка идёт по карточке приложения. А вот поле scope уже выданной пары останется прежним: оно снято в момент выдачи и переносится при обновлении по refresh_token. Свежий набор придёт с новой парой — например, при следующем открытии фрейма.

ERROR_PLACEMENT_NOT_FOUND

placement.bind отвечает так и на неизвестный код, и на код, право для которого приложению не выдано. Боевой портал эти случаи тоже не разделяет. Сверяйте код вызовом placement.list — он показывает только те точки, которые доступны этому приложению.

Рядом ещё две ошибки того же метода: ERROR_UNSUPPORTED_PROTOCOL, если адрес обработчика не http и не https, и ERROR_PLACEMENT_MAX_COUNT, если для точки уже зарегистрировано столько обработчиков, сколько она допускает.

Про localhost в placement.bind

Единственное намеренное отличие от боевого портала: ERROR_WRONG_HANDLER_URL на локальный адрес здесь не выдаётся. Ради этого продукт и существует.

Виджет зарегистрирован, но его нет

placement.bind вернул true, регистрация видна в placement.get, вкладки в карточке сделки нет. Значит, приложение не завершило установку: до BX24.installFinish() портал не показывает виджеты и не доставляет события. Проверяется полем INSTALLED в app.info.

Ответ не тот, что ожидался, а метод портальный

Заголовок X-Mock-Scenario действует раньше контекста приложения: при любом сценарии, кроме success, отвечает шлюз, а не портал. app.info со сценарием rate_limit вернёт QUERY_LIMIT_EXCEEDED, а не состояние приложения. Тот же эффект даёт ненулевая доля случайных ошибок на экране «Ключи и токены» — она действует на весь шлюз, включая placement.bind и event.bind.

redirect_uri уходит куда попросили

APIStend не сверяет redirect_uri с карточкой приложения и вернёт код по любому переданному адресу. Боевой портал так не делает: он redirect_uri в запросе не принимает вовсе и всегда возвращает код на адрес из карточки. Приложение, которое здесь получает код на «удобный» адрес, в бою получит его на другой — проверяйте цикл с тем адресом, который стоит в карточке.