Разработка и поддержка HTTP-сервисов в среде «1С:Предприятие» — мощный инструмент для интеграции с внешними системами. Однако разработчики часто сталкиваются со специфическим поведением платформы: после внесения изменений в код модуля HTTP-сервиса при попытке вызвать любой метод система возвращает ошибку «Ошибка инициализации библиотеки модулей» (Error 500). При этом стандартное обновление конфигурации базы данных проходит без каких-либо предупреждений, а встроенный синтаксический контроль уверяет, что «ошибок не обнаружено».
В этой статье мы подробно разберем, почему возникает эта ошибка, как настроить Конфигуратор для обнаружения скрытых синтаксических дефектов и какие еще подводные камни могут приводить к падению модуля HTTP-сервиса.
Для начала проанализируем поведение платформы «1С:Предприятие» при работе с HTTP- и Web-сервисами. Когда мы пишем код в модуле обычного приложения или управляемой формы, Конфигуратор проверяет синтаксис «на лету» или при сохранении. Но с модулями HTTP-сервисов ситуация иная:
В рассматриваемой ситуации автор темы столкнулся с банальной, но труднозаметной опечаткой — точкой с запятой после ключевого слова КонецФункции:
Функция ПолучитьКоличествоСвободныхНомеровПоКатегориям(ДатаНачала, ДатаОкончания)
// ...код функции...
КонецФункции; // Ошибка здесь! Точка с запятой после КонецФункции недопустима
Обычный синтаксический контроль пропустил эту ошибку, но при вызове HTTP-сервиса платформа попыталась скомпилировать модуль на сервере, споткнулась о лишний символ и вернула ошибку инициализации библиотеки.
Чтобы больше не тратить часы на поиск пропущенных запятых или лишних символов, нам необходимо принудительно заставить Конфигуратор проверять код в контексте сервера. Выполним следующие настройки:
Теперь выполним проверку непосредственно в модуле HTTP-сервиса:
Помимо банальных опечаток, существует целый ряд архитектурных причин, по которым модуль HTTP-сервиса может выдавать ошибку инициализации. Рассмотрим код, который изначально привел к ошибке в обсуждении:
Функция ПолучитьКоличествоСвободныхНомеровПоКатегориям(ДатаНачала, ДатаОкончания)
// Получим список занятых номеров
ЗапросЗанятых = Новый Запрос;
ЗапросЗанятых.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| СостояниеНомеров.Номер КАК Номер
|ИЗ
| РегистрСведений.СостояниеНомеров КАК СостояниеНомеров
|ГДЕ
| (СостояниеНомеров.ДатаНачала МЕЖДУ &ДатаНачала И &ДатаОкончания
| ИЛИ СостояниеНомеров.ДатаОкончания МЕЖДУ &ДатаНачала И &ДатаОкончания
| ИЛИ (СостояниеНомеров.ДатаНачала <= &ДатаНачала
| И СостояниеНомеров.ДатаОкончания >= &ДатаОкончания))
| И СостояниеНомеров.Состояние В(&СписокСостояний)";
ЗапросЗанятых.УстановитьПараметр("ДатаНачала", ДатаНачала);
ЗапросЗанятых.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
СписокСостояний = Новый Массив;
СписокСостояний.Добавить(Перечисления.СостоянияНомеров.Забронирован);
СписокСостояний.Добавить(Перечисления.СостоянияНомеров.Заселен);
СписокСостояний.Добавить(Перечисления.СостоянияНомеров.Выехал);
ЗапросЗанятых.УстановитьПараметр("СписокСостояний", СписокСостояний);
СписокЗанятыхНомеров = ЗапросЗанятых.Выполнить().Выгрузить().ВыгрузитьКолонку("Номер");
// Получим список доступных номеров
ЗапросСвободных = Новый Запрос;
ЗапросСвободных.Текст =
"ВЫБРАТЬ
| СправочникОтделы.Ссылка КАК Ссылка,
| СправочникОтделы.Код КАК Код,
| СправочникОтделы.Наименование КАК Наименование,
| СправочникОтделы.КатегорияНомера КАК КатегорияНомера,
| СправочникОтделы.КодФиделио КАК КодФиделио
|ИЗ
| Справочник.Отделы КАК СправочникОтделы
|ГДЕ
| СправочникОтделы.КатегорияНомера <> ЗНАЧЕНИЕ(Справочник.КатегорииНомеровГостиницы.ПустаяСсылка)
| И СправочникОтделы.ДатаНач <> ДАТАВРЕМЯ(1, 1, 1)
| И НЕ СправочникОтделы.Ссылка В (&СписокЗанятыхНомеров)";
ЗапросСвободных.УстановитьПараметр("СписокЗанятыхНомеров", СписокЗанятыхНомеров);
СвободныеНомера = ЗапросСвободных.Выполнить().Выгрузить();
Возврат СвободныеНомера; // Внимание: Возврат ТаблицыЗначений!
КонецФункции
Проанализируем этот код. Функция возвращает объект типа ТаблицаЗначений (переменная СвободныеНомера). И хотя в данном случае падение происходило из-за точки с запятой, возвращать таблицу значений напрямую из методов HTTP-сервиса — крайне плохая практика, которая также может приводить к ошибкам.
Дело в том, что HTTP-сервис общается с внешним миром посредством текстовых или бинарных данных (обычно JSON или XML). Объекты типа ТаблицаЗначений, СправочникСсылка, ДокументСсылка не могут быть автоматически сериализованы платформой при отправке ответа клиенту. Если вы попытаетесь передать такой объект наружу, вы гарантированно получите ошибку выполнения.
Правильным подходом будет преобразование полученной таблицы значений в массив структур и последующая сериализация в JSON. Рассмотрим, как переписать этот участок кода:
// Функция преобразует ТаблицуЗначений в массив структур для последующей сериализации
Функция ТаблицаЗначенийВМассивСтруктур(ТаблицаДанных)
МассивРезультат = Новый Массив;
Колонки = ТаблицаДанных.Колонки;
Для Каждого СтрокаТаблицы Из ТаблицаДанных Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из Колонки Цикл
// Для ссылочных типов получаем строковое представление или уникальный идентификатор
ЗначениеПоля = СтрокаТаблицы[Колонка.Имя];
Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(ЗначениеПоля)) Тогда
СтруктураСтроки.Вставить(Колонка.Имя, Строка(ЗначениеПоля));
Иначе
СтруктураСтроки.Вставить(Колонка.Имя, ЗначениеПоля);
КонецЕсли;
КонецЦикла;
МассивРезультат.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
После этого в самом методе обработки HTTP-запроса мы сможем легко преобразовать полученный массив структур в формат JSON и вернуть его клиенту:
// Пример метода-обработчика GET запроса
Функция ПолучитьСвободныеНомера(Запрос)
ДатаНач = Запрос.ПараметрыURL.Получить("ДатаНачала");
ДатаКон = Запрос.ПараметрыURL.Получить("ДатаОкончания");
// Вызываем нашу функцию получения данных
ТаблицаНомеров = ПолучитьКоличествоСвободныхНомеровПоКатегориям(ДатаНач, ДатаКон);
МассивДанных = ТаблицаЗначенийВМассивСтруктур(ТаблицаНомеров);
// Сериализуем в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивДанных);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Формируем HTTP-ответ
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Возврат Ответ;
КонецФункции
Если синтаксическая проверка настроена верно, опечаток нет, но ошибка «Ошибка инициализации библиотеки модулей» все равно возникает, проанализируйте ваш код на наличие следующих проблем:
Сообщить(), Предупреждение(), Вопрос(), ОткрытьФорму(). Наличие этих функций приведет к моментальному отказу компиляции модуля.
ЗначениеВСтрокуВнутр() или ЗначениеИзСтрокиВнутр() непосредственно внутри модуля HTTP-сервиса часто вызывает сбой компиляции. Если вам действительно необходимы эти функции, вынесите логику с ними в отдельный серверный общий модуль и вызывайте его.
default.vrd или передаваемый через Basic Auth), должен обладать правами на выполнение этого конкретного HTTP-сервиса, а также правами чтения на все объекты метаданных, к которым обращается код сервиса (в нашем примере — к регистру сведений СостояниеНомеров и справочнику Отделы). Если прав недостаточно, сервер вернет ошибку авторизации или ошибку инициализации библиотек.
Для быстрой и эффективной диагностики проблем при разработке интеграционных решений рекомендуем использовать следующие инструменты:
-debug). В Конфигураторе зайдите в меню Отладка — Подключение..., нажмите кнопку Автоматическое подключение и установите флажок для HTTP-сервисы. После этого точки останова, установленные в модуле сервиса, начнут срабатывать при внешних запросах.
Применяя описанные методики настройки синтаксического контроля и следуя правилам разработки серверного кода, вы сможете быстро находить и устранять любые проблемы при работе с HTTP-сервисами в 1С.