Стандартный интерфейс OData в платформе «1С:Предприятие 8» является мощным инструментом для интеграции со сторонними системами. Однако разработчики часто сталкиваются с ситуацией, когда при отправке вполне корректного, на первый взгляд, запроса PATCH или POST сервер возвращает лаконичную ошибку 500 Internal Server Error без какого-либо внятного описания в теле ответа. Это превращает отладку интеграции в настоящее испытание.
В этой статье мы подробно разберем две скрытые особенности работы OData, которые чаще всего приводят к падению веб-сервиса с ошибкой 500: хаотичную нумерацию строк (LineNumber) в табличных частях и особенности сериализации реквизитов с типом ХранилищеЗначения (например, настроек компоновки данных).
---
При получении данных документа с помощью обычного GET-запроса мы ожидаем увидеть строгий и упорядоченный массив строк табличной части, где поле LineNumber (НомерСтроки) возрастает последовательно: 1, 2, 3 и так далее. Однако на практике (особенно в конфигурациях вроде «Управление торговлей», «ERP» или «Комплексная автоматизация») OData может выдать совершенно непредсказуемую последовательность.
Рассмотрим реальный пример порядка индексов LineNumber, возвращаемых платформой:
1, 1, 2, 3, 5, 4, 5, 6, 9, 7, 8, 9, 13, 10, 11, 12, 17, 13, 14, 15...
Мы видим здесь полный набор аномалий: дублирование номеров строк, пропуски индексов и нарушение естественного порядка сортировки. При этом, если мы обратимся напрямую к СУБД (например, PostgreSQL или MS SQL Server) и сделаем прямой запрос к таблице этой табличной части, мы увидим идеальную картину:
SELECT
pspk."НомерСтроки",
pspk._keyfield
FROM names."Документ_УстановкаЦенНоменклатуры_Товары" pspk
WHERE pspk."Ссылка_Ссылка" = '\\x01239740-7a6a-11e6-f487-1efb12b8561c';
В результате СУБД вернет записи, где физическое поле LineNo (НомерСтроки) и внутренний ключ _keyfield идут в строгом порядке от 1 до 20 и от 0x00 до 0x13 соответственно. Почему же OData отдает искаженные данные?
Дело в том, что при генерации SQL-запроса платформенный генератор OData в ряде случаев не накладывает жесткую сортировку по индексу строки. В итоге СУБД возвращает строки в порядке их физического расположения на диске или в зависимости от кэша, плана запроса и используемого индекса. При сериализации этого неупорядоченного набора в JSON/XML значения LineNumber могут «перемешаться».
Почему происходит ошибка 500 при отправке PATCH?
Если мы попытаемся отправить этот же полученный массив строк обратно в базу через метод PATCH, не изменяя структуру (или изменив только пару реквизитов), платформа 1С запустит механизм валидации табличной части. При записи объекта встроенный язык 1С требует, чтобы номера строк были уникальными, шли строго по порядку и начинались с 1. Обнаружив дубли и пропуски в свойстве LineNumber, транзакция записи аварийно завершается с внутренним исключением. Веб-сервер перехватывает этот сбой и отдает внешнему клиенту общую ошибку 500.
Как решить проблему с LineNumber:
PATCH, ваше внешнее приложение должно в цикле перезаписать поле LineNumber для всех элементов массива строго по порядку, начиная с 1.Проанализируем пример кода на JavaScript, выполняющий подготовку данных перед отправкой:
function prepareTableSection(rows) {
return rows.map((row, index) => {
// Копируем свойства строки и принудительно задаем правильный номер
return {
...row,
LineNumber: (index + 1).toString()
};
});
}
После такой предобработки валидатор платформы успешно примет структуру табличной части, и документ запишется без ошибок.
---
Вторая неочевидная причина падения OData-запросов связана с реквизитами, имеющими тип ХранилищеЗначения. В типовых конфигурациях это поле часто используется для хранения настроек, отчетов, картинок или файлов (например, свойство НастройкиКомпоновкиДанных в различных аналитических регистрах и документах).
При чтении данных через GET-запрос платформа OData представляет одно поле типа ХранилищеЗначения в виде трех отдельных свойств:
НастройкиКомпоновкиДанных) — строковое представление структуры или навигационная ссылка.НастройкиКомпоновкиДанных_Base64Data) — непосредственно бинарные данные, упакованные в формат Base64.НастройкиКомпоновкиДанных_Type) — метаданные, описывающие MIME-тип содержимого.Почему происходит сбой при записи?
Если при отправке PATCH-запроса вы передадите в JSON-пакете базовое свойство (даже если оно пустое или содержит корректный XML/JSON) одновременно со свойством _Base64Data, сериализатор XDTO на стороне 1С столкнется с конфликтом десериализации. Платформа попытается прочитать оба поля как независимые источники данных для одного реквизита, что приведет к критической ошибке разбора типов и возврату ошибки 500.
Правильное решение для ХранилищаЗначения:
При изменении данных в таких полях необходимо полностью удалять из тела запроса базовое свойство и свойство типа, оставляя исключительно свойство с суффиксом _Base64Data.
Рассмотрим структуру «плохого» и «хорошего» JSON-запроса.
Неправильно (вызовет ошибку 500):
{
"Ref_Key": "01239740-7a6a-11e6-f487-1efb12b8561c",
"НастройкиКомпоновкиДанных": "Значение...",
"НастройкиКомпоновкиДанных_Base64Data": "eyJDdXN0b21lciI6ICJKb2huIERvZSJ9",
"НастройкиКомпоновкиДанных_Type": "application/json"
}
Правильно (запрос выполнится успешно):
{
"Ref_Key": "01239740-7a6a-11e6-f487-1efb12b8561c",
"НастройкиКомпоновкиДанных_Base64Data": "eyJDdXN0b21lciI6ICJKb2huIERvZSJ9"
}
---
Если вы применили указанные выше рекомендации, но запросы все равно завершаются с ошибкой 500, необходимо заглянуть внутрь «черного ящика» платформы 1С. Для этого существуют два надежных инструмента диагностики.
Технологический журнал позволяет перехватить реальные исключения встроенного языка, которые происходят в момент записи объекта веб-сервером. Создадим или отредактируем файл конфигурации ТЖ logcfg.xml, добавив в него сбор событий EXCP (исключения) и ERR с фильтрацией по процессу rphost:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\v8\logs" keep="24">
<event>
<eq property="Name" value="EXCP"/>
</event>
<event>
<eq property="Name" value="ERR"/>
</event>
<property name="AllProperties"/>
</log>
</config>
После этого воспроизведем ошибку 500. В логах технологического журнала появится детальный стек вызовов, в котором будет четко указано, какая именно строка кода или какое бизнес-правило (например, проверка в модуле объекта ПередЗаписью) привели к транзакционному сбою.
По умолчанию IIS или Apache перехватывают детальные ответы от модуля расширения веб-сервисов 1С и маскируют их, отдавая внешнему клиенту пустую страницу 500. Чтобы заставить веб-сервер пропускать оригинальные сообщения об ошибках от платформы 1С, выполним следующие настройки:
web.config вашего виртуального приложения 1С в секции <system.webServer> укажем параметр existingResponse="PassThrough" для тега httpErrors.ProxyPass или модули маршрутизации не переопределяют заголовки ответов с кодами состояния 5xx.После включения сквозной передачи ошибок веб-сервер начнет отдавать структурированный XML или JSON-ответ, содержащий детальное описание ошибки от самого сервера приложений 1С (например, конфликт типов XDTO или текст ошибки блокировки базы данных).
Резюмируя: при интеграции с 1С через стандартный интерфейс OData всегда уделяйте повышенное внимание ручной подготовке коллекций табличных частей и правильной передаче двоичных данных. Эти простые шаги уберегут вашу интеграцию от неожиданных падений и обеспечат стабильный обмен данными.