При активном внедрении интеграционных механизмов в СУБД-экосистему платформы «1С:Предприятие» разработчики часто выбирают стандартный REST-интерфейс OData. Он позволяет быстро и без написания кода получить доступ к метаданным конфигурации. Однако при работе со старыми конфигурациями (например, находящимися на режиме совместимости 8.3.4, 8.2 или ниже) можно столкнуться с крайне неочевидной ошибкой при попытке прочитать значение любой константы.
Запрос вида http://host/base/odata/standard.odata/Constant_ИмяКонстанты?$format=json внезапно возвращает ошибку СУБД:
{
"odata.error": {
"code": "-1",
"message": {
"lang": "ru",
"value": "{(1, 30)}: Таблица не найдена \"Constant.ИмяКонстанты\"\nSELECT sourceAlias.* FROM <<?>>Constant.ИмяКонстанты AS sourceAlias"
}
}
}
При этом обращение к справочникам или документам по аналогичным адресам работает абсолютно корректно и стабильно. В рамках этой статьи мы подробно разберем, почему возникает данная проблема, как особенности хранения данных в СУБД влияют на поведение OData и какими способами можно обойти это платформенное ограничение.
Чтобы понять природу этой ошибки, проанализируем эволюцию архитектуры платформы 1С. Ключевой фактор здесь — это физическая структура хранения констант в базе данных на уровне СУБД (MS SQL Server, PostgreSQL и др.):
_Consts). Каждая константа представляла собой отдельную колонку (поле) внутри этой общей таблицы._Const1234).Однако то, какая именно структура хранения будет использоваться в вашей базе данных, напрямую зависит от настройки «Режим совместимости» в свойствах конфигурации:
Интерфейс OData (стандартный REST-интерфейс 1С) проектировался и оптимизировался под новые стандарты платформы. При обработке запроса к ресурсу Constant_ИмяКонстанты транслятор OData генерирует низкоуровневый SQL-запрос, ориентируясь на современную схему данных. Он пытается обратиться к таблице Constant.ИмяКонстанты, ожидая, что она существует отдельно. Но поскольку база данных работает в старом режиме совместимости, СУБД возвращает закономерный ответ: «Таблица не найдена».
При этом в XML-описании метаданных по адресу .../odata/standard.odata/$metadata константа будет отображаться как существующий тип данных. Происходит это потому, что генератор метаданных анализирует логическую структуру конфигурации, а СУБД-исполнитель пытается выполнить запрос физически.
Рассмотрим подробнее три практических метода решения этой проблемы, начиная от кардинального изменения настроек системы и заканчивая программными обходными путями.
---
Самый очевидный способ решить проблему «в лоб» — обновить режим совместимости конфигурации до актуального (или как минимум до значения 8.3.5). Это заставит платформу реструктуризировать базу данных и выделить для каждой константы индивидуальную таблицу.
Также повышение режима совместимости до 8.3.5 откроет вам доступ к встроенной функции управления составом OData:
тМассив = Новый Массив;
тМассив.Добавить(Метаданные.Константы.ДатаНачалаАвтоматическогоОтраженияВУчетеНДС);
УстановитьСоставСтандартногоИнтерфейсаOData(тМассив);
В режимах совместимости 8.3.4 и ниже попытка выполнить этот код вызовет ошибку, сообщающую, что метод УстановитьСоставСтандартногоИнтерфейсаOData недоступен.
Важное предупреждение: переход на новый режим совместимости для старых и сильно доработанных конфигураций (таких как БП 2.0 КОРП или УПП 1.3) сопряжен с огромными рисками. В платформе версии 8.3.5 были изменены правила выполнения серверных вызовов при закрытии форм, изменены некоторые механизмы блокировок и удалены устаревшие функции. Изменение режима совместимости потребует от вас тотального тестирования всей конфигурации и исправления множества сопутствующих ошибок в коде.
---
Если менять режим совместимости всей информационной базы категорически запрещено из соображений стабильности, наилучшим решением будет отказаться от OData для чтения констант и разработать собственный простой HTTP-сервис.
Давайте разберем по шагам, как быстро реализовать такой сервис в конфигураторе:
ПолучениеДанных)./v1).GetConstant и шаблоном пути /constants/{ИмяКонстанты}.GET и назначьте ему обработчик в модуле HTTP-сервиса.Ниже представлен универсальный пример программного кода для чтения констант через ваш новый HTTP-сервис:
Функция ПолучитьКонстантуGET(Запрос)
ИмяКонстанты = Запрос.ПараметрыURL.Получить("ИмяКонстанты");
// Проверяем наличие константы в метаданных для защиты от некорректных запросов
МетаданноеКонстанты = Метаданные.Константы.Найти(ИмяКонстанты);
Если МетаданноеКонстанты = Неопределено Тогда
Ответ = Новый HTTPСервисОтвет(404);
Ответ.УстановитьТелоИзСтроки("Константа '" + ИмяКонстанты + "' не найдена в метаданных.");
Возврат Ответ;
КонецЕсли;
// Получаем значение константы динамически
ЗначениеКонстанты = Константы[ИмяКонстанты].Получить();
// Формируем структуру ответа в формате JSON
СтруктураОтвета = Новый Структура;
СтруктураОтвета.Вставить("Name", ИмяКонстанты);
СтруктураОтвета.Вставить("Value", ЗначениеКонстанты);
// Сериализуем данные в строку JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураОтвета);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Формируем успешный HTTP-ответ
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Ответ.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Возврат Ответ;
КонецФункции
Этот способ абсолютно безопасен, не зависит от режима совместимости, работает чрезвычайно быстро и позволяет гибко обрабатывать типы данных (например, преобразовывать даты или ссылки в нужный вызывающей системе формат).
---
Если внешняя система жестко завязана именно на протокол OData и не может быть перенастроена на работу с другими HTTP-адресами, мы можем прибегнуть к архитектурному обходу проблемы внутри 1С. Для этого мы будем дублировать значения нужных констант в специально созданный Регистр Сведений, к которому OData сможет обращаться без каких-либо ошибок.
Проанализируем по шагам процесс реализации этого обходного пути:
БуферКонстант.ИмяКонстанты (тип: Строка, длина 150, индексировать).Значение (тип: ЛюбаяСсылка, Число, Строка, Дата, Булево — настройте составной тип данных в зависимости от типов ваших констант).Теперь нам необходимо обеспечить актуальность данных в этом регистре. Для этого мы напишем подписку на событие или внесем изменения непосредственно в модуль менеджера константы. Поскольку константы меняются нечасто, этот код не создаст никакой нагрузки на систему.
Пример кода для записи значения константы в наш регистр-буфер:
Процедура ЗаписатьКонстантуВБуфер(ИмяКонстанты, ЗначениеКонстанты) Экспорт
МенеджерЗаписи = РегистрыСведений.БуферКонстант.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИмяКонстанты = ИмяКонстанты;
// Считываем текущую запись, чтобы не перезаписывать данные без необходимости
МенеджерЗаписи.Прочитать();
Если Не МенеджерЗаписи.Выбран() Или МенеджерЗаписи.Значение <> ЗначениеКонстанты Тогда
МенеджерЗаписи.ИмяКонстанты = ИмяКонстанты;
МенеджерЗаписи.Значение = ЗначениеКонстанты;
МенеджерЗаписи.Записать(Истина);
КонецЕсли;
КонецПроцедуры
Этот метод регистрации изменений позволит вам получать актуальные данные констант через стандартный REST-запрос OData к регистру сведений:
http://host/base/odata/standard.odata/InformationRegister_БуферКонстант(ИмяКонстанты='ДатаНачалаАвтоматическогоОтраженияВУчетеНДС')?$format=json
Мы выяснили истинную причину ошибки — конфликт между ожидаемой физической структурой OData и реальным устройством таблиц СУБД в режиме совместимости 8.3.4 и ниже. Для решения этой проблемы вы можете выбрать любой из предложенных вариантов в зависимости от жесткости требований к стабильности вашей конфигурации 1С. Использование собственного HTTP-сервиса видится наиболее надежным и современным подходом, исключающим необходимость внесения опасных изменений в свойства совместимости конфигурации.