Разработка двустороннего обмена данными между базами 1С или между 1С и сторонним сайтом — одна из самых частых задач современного программиста. Использование технологии HTTP-сервисов и формата передачи данных JSON является промышленным стандартом. Однако на начальном этапе разработчики часто сталкиваются с классическими архитектурными ошибками, путаницей в HTTP-методах (GET/POST) и проблемами с авторизацией (ошибка 401 Unauthorized).
В этой статье мы подробно разберем, как правильно организовать получение данных из HTTP-сервиса 1С, как настроить авторизацию без ошибок и как использовать современные встроенные механизмы платформы 1С для работы с запросами и парсинга JSON.
Рассмотрим ситуацию, с которой столкнулся автор темы. На стороне базы-источника опубликован HTTP-сервис с методом GET. При обращении через обычный браузер по адресу http://localhost/Test_HTTP_1/hs/info?action=getrooms он успешно получает данные в формате JSON. Но при попытке прочитать эти же данные из другой базы 1С с помощью программного кода возникает ошибка 401 Unauthorized.
Проанализируем исходный код автора, содержащий критические ошибки:
Процедура КнопкаВыполнитьНажатие(Кнопка)
Адрес = "http://localhost/Test_HTTP_1/hs/info?action=getrooms";
ПарамОтвет = Новый Структура;
ВыполнитьHTTPЗапрос(Адрес, "", "POST", ПарамОтвет);
Если Сред(ПарамОтвет.Ошибка, 1, 28) = "КодСостояния HTTP-запроса: 2" Тогда
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ПарамОтвет.Ответ);
Структ = ПрочитатьJSON(Чтение, Истина);
Иначе
Сообщить(ПарамОтвет.Ошибка)
КонецЕсли;
КонецПроцедуры
В этом примере кода отчетливо видны три проблемы:
GET (что подтверждается успешным открытием в браузере), а программный запрос отправляется методом POST. В концепции REST API метод GET используется исключительно для безопасного и неизменяемого чтения данных, тогда как POST служит для отправки новой информации на сервер. Пытаться прочитать данные методом POST с ресурса, настроенного только на GET, — архитектурная ошибка.ВыполнитьHTTPЗапрос является устаревшей самописной процедурой. Современная платформа 1С v8.3 предоставляет мощные и удобные объектные механизмы: HTTPСоединение, HTTPЗапрос и HTTPОтвет, которые делают код прозрачным и легким для отладки.Для того чтобы принимающая база 1С могла успешно авторизоваться на веб-сервере, нам нужно передать учетные данные. Существует два основных способа решения этой проблемы.
Способ А. Передача учетных данных через HTTPСоединение (рекомендуется)
Самый надежный и безопасный вариант — использовать Basic-авторизацию, передавая имя пользователя и пароль непосредственно при инициализации соединения. Обратите внимание на важный нюанс: имя пользователя в 1С для веб-обменов настоятельно рекомендуется писать латиницей. Использование кириллических логинов часто приводит к искажению кодировки при конвертации заголовков на стороне веб-сервера.
Способ Б. Настройка файла публикации default.vrd
Если веб-сервис должен отдавать данные без требования ввода логина и пароля со стороны клиента (например, при интеграции с внешним сайтом, который не умеет хранить сложные реквизиты авторизации), можно прописать системного пользователя в файле публикации default.vrd. Для этого в строке подключения информационной базы укажите параметры usr и pwd:
<ib connection="Srvr="server1c";Ref="test_db";usr="WebUser";pwd="StrongPassword123";"/>
В этом случае любые внешние запросы к публикации будут автоматически выполняться в контексте прав пользователя WebUser. Тем не менее, для обмена "1С-to-1С" первый способ с явным указанием учетных данных в коде является более предпочтительным и безопасным.
Давайте откажемся от использования устаревших функций-оберток и напишем современный, чистый код на платформе 1С v8.3. Мы разберем процесс по шагам: создание соединения, отправка GET-запроса, разбор полученного JSON-ответа.
&НаКлиенте
Процедура ПолучитьДанныеОНомерах(Команда)
// Выносим тяжелые серверные вызовы на сервер
ПолучитьДанныеОНомерахНаСервере();
КонецПроцедуры
&НаСервере
Процедура ПолучитьДанныеОНомерахНаСервере()
// Шаг 1. Определяем параметры подключения к веб-серверу
Сервер = "localhost";
Порт = 80; // Для HTTPS обычно используется 443
Пользователь = "WebExchangeUser";
Пароль = "MySecurePassword125";
// Шаг 2. Создаем HTTP-соединение с авторизацией
// За счет передачи логина и пароля мы полностью избегаем ошибки 401
Соединение = Новый HTTPСоединение(Сервер, Порт, Пользователь, Пароль);
// Шаг 3. Формируем относительный адрес ресурса (URI)
// Метод GET позволяет передавать параметры прямо в строке запроса после знака "?"
АдресРесурса = "/Test_HTTP_1/hs/info?action=getrooms";
Запрос = Новый HTTPЗапрос(АдресРесурса);
// При необходимости можно явно задать заголовки, сообщающие серверу, какой тип контента мы ожидаем
Запрос.Заголовки.Вставить("Accept", "application/json");
Попытка
// Шаг 4. Выполняем GET-запрос
Ответ = Соединение.Получить(Запрос);
// Шаг 5. Анализируем код состояния ответа сервера
Если Ответ.КодСостояния = 200 Тогда // 200 OK — запрос выполнен успешно
// Получаем тело ответа в виде строки в кодировке UTF-8
СтрокаJSON = Ответ.ПолучитьТелоКакСтроку("UTF-8");
// Переходим к разбору JSON-структуры
ОбработатьПолученныйJSON(СтрокаJSON);
Иначе
// Обрабатываем другие коды состояния (например, 401, 404, 500)
ЗаписьЖурналаРегистрации("HTTP-Обмен", УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось получить данные. Код состояния сервера: " + Ответ.КодСостояния);
ВызватьИсключение "Ошибка обмена! Сервер вернул код: " + Ответ.КодСостояния;
КонецЕсли;
Исключение
// Перехватываем сетевые ошибки (недоступность сервера, таймаут и т.д.)
ЗаписьЖурналаРегистрации("HTTP-Обмен", УровеньЖурналаРегистрации.Ошибка, , ,
"Сетевая ошибка при выполнении запроса: " + ОписаниеОшибки());
ВызватьИсключение "Ошибка сетевого соединения с сервером!";
КонецПопытки;
КонецПроцедуры
После того как мы успешно получили строку JSON, её необходимо преобразовать в понятные для 1С структуры данных (Массивы, Соответствия или Структуры). Разберем, как правильно использовать встроенный объект ЧтениеJSON.
Посмотрим на структуру JSON, которую прислал сервер в примере автора:
[
{
"date_begin": "2019.10.10",
"date_end": "2019.10.16",
"seats": 2,
"seats_child": 0,
"check_in": 1,
"check_out": 1,
"nutrition": "hb"
}
]
Данный JSON представляет собой массив объектов. Напишем процедуру для разбора этой структуры:
&НаСервере
Процедура ОбработатьПолученныйJSON(Знач СтрокаJSON)
// Инициализируем объект чтения JSON
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(СтрокаJSON);
// Читаем JSON в переменные 1С.
// Второй параметр "Ложь" указывает, что объекты JSON преобразуются в Структуры 1С.
// Если ключи в JSON содержат недопустимые для свойств Структуры символы (например, дефисы),
// следует передать "Истина", тогда объекты преобразуются в Соответствия.
ДанныеОбмена = ПрочитатьJSON(Чтение, Ложь);
Чтение.Закрыть();
// Поскольку корень нашего JSON — это массив (обозначен квадратными скобками []),
// в 1С мы получили переменную с типом "Массив", элементами которого являются "Структуры".
Если ТипЗнч(ДанныеОбмена) = Тип("Массив") Тогда
Для Каждого ДанныеНомера Из ДанныеОбмена Цикл
// Извлекаем свойства из структуры
ДатаНачалаСтрока = ДанныеНомера.date_begin; // Получаем строку "2019.10.10"
ДатаОкончанияСтрока = ДанныеНомера.date_end; // Получаем строку "2019.10.16"
КоличествоМест = ДанныеНомера.seats; // Получаем Число 2
Питание = ДанныеНомера.nutrition; // Получаем Строку "hb"
// В JSON даты передаются как строки. Преобразуем их в тип Дата 1С вручную
ДатаНачала = ПреобразоватьСтрокуВДату(ДатаНачалаСтрока);
ДатаОкончания = ПреобразоватьСтрокуВДату(ДатаОкончанияСтрока);
// Здесь пишем бизнес-логику: запись в регистр сведений, создание документов "Бронирование" и т.д.
Сообщить(СтрШаблон("Успешно обработан номер. Период: %1 - %2. Мест: %3",
Формат(ДатаНачала, "ДФ=dd.MM.yyyy"),
Формат(ДатаОкончания, "ДФ=dd.MM.yyyy"),
КоличествоМест));
КонецЦикла;
Иначе
ВызватьИсключение "Неверный формат данных: ожидался массив объектов.";
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПреобразоватьСтрокуВДату(Знач СтрокаДаты)
// Преобразует формат "ГГГГ.ММ.ДД" в стандартную дату 1С.
// Очищаем строку от возможных пробелов
СтрокаДаты = СокрЛП(СтрокаДаты);
Если СтрДлина(СтрокаДаты) = 10 Тогда
ЧастиДаты = СтрРазделить(СтрокаДаты, ".");
Если ЧастиДаты.Количество() = 3 Тогда
Возврат Дата(ЧастиДаты[0], ЧастиДаты[1], ЧастиДаты[2]);
КонецЕсли;
КонецЕсли;
Возврат '00010101'; // Если дата не распознана, возвращаем пустую дату
КонецФункции
Для создания надежного и отказоустойчивого двустороннего обмена рекомендуем придерживаться следующих правил:
HTTPСоединение обязательно передавайте время ожидания ответа (параметр Таймаут). По умолчанию он равен 0 (бесконечное ожидание), что при зависании внешнего сайта может полностью заблокировать работу фонового задания 1С. Оптимальное значение — 10-15 секунд.Попытка...Исключение и записывайте подробности сетевых сбоев в Журнал регистрации. Это сбережет часы отладки при падении каналов связи или падении веб-сервера.ПрочитатьJSON вызовет падение вашей программы. Всегда проверяйте соответствие заголовка Content-Type значению application/json перед чтением тела ответа.Используя предложенные методики и современные объекты платформы 1С, вы сможете быстро и элегантно развернуть стабильный обмен данными, не прибегая к использованию непрозрачных сторонних библиотек и устаревших функций.