При работе с устаревшими версиями программного обеспечения, такими как 1С 7.7, особенно при попытках интеграции с современными веб-сервисами, мы часто сталкиваемся с рядом специфических проблем. Одной из таких задач является выгрузка большого объема данных, например, списка товаров, на внешний сайт (в данном случае, WooCommerce) через JSON-API. Ситуация усложняется, когда сервер 1С работает на старой операционной системе (например, Windows Server 2003), которая не поддерживает актуальные версии протокола HTTPS, необходимые для безопасного соединения с современными веб-ресурсами. В такой конфигурации многие разработчики прибегают к использованию прокси-сервера, например, на базе Flask, чтобы обойти ограничения HTTPS и осуществить передачу данных.
Однако даже при наличии прокси-сервера могут возникать непредвиденные проблемы. В этой статье мы подробно рассмотрим случай, когда при выполнении серии последовательных (синхронных) POST-запросов из 1С 7.7, лишь последний запрос из серии отрабатывает корректно, содержа в себе тело JSON, тогда как все предыдущие запросы приходят на целевой сервер с пустым телом, вызывая ошибки HTTP 400 "Bad Request". Мы проанализируем возможные причины такого поведения и предложим комплексные решения.
Наш сценарий предполагает следующую цепочку взаимодействия:
Автор проблемы, столкнувшийся с описанной ситуацией, предоставил фрагменты логов, подтверждающие это: большинство запросов завершаются с кодом 400 и отсутствующим телом, тогда как один из запросов проходит успешно с кодом 200 и полным JSON-телом:
10.0.0.2 - - [29/Oct/2020 22:31:59] "POST /wp-json/wc/v3/products/batch/ HTTP/1.1" 400 -
db.test.ru
ck_693982c64ef09d3870cf5f8ec2b353e5eef37056
cs_062c1995b091e17f70e4087b9499264fb892edbb
OLOLO
... (много подобных записей с 400) ...
10.0.0.2 - - [29/Oct/2020 22:31:59] "POST /wp-json/wc/v3/products/batch/ HTTP/1.1" 400 -
db.test.ru
ck_693982c64ef09d3870cf5f8ec2b353e5eef37056
cs_062c1995b091e17f70e4087b9499264fb892edbb
OLOLO
{'create': [{'type': 'simple', 'regular_price': '4322.00', 'status': 'publish', 'tax_status': 'none', 'virtual': False, 'catalog_visibility': 'visible', 'featured': False, 'manage_stock': True, 'sku': '', 'downloadable': False, 'name': 'Абажур подвесной Паутина А-33'}]}
https://db.test.ru/wp-json/wc/v3/products/batch/?consumer_key=ck_693982c64ef09d3870cf5f8ec2b353e5eef37056&consumer_secret=cs_062c1995b091e17f70e4087b9499264fb892edbb&;
with body {'create': [{'type': 'simple', 'regular_price': '4322.00', 'status': 'publish', 'tax_status': 'none', 'virtual': False, 'catalog_visibility': 'visible', 'featured': False, 'manage_stock': True, 'sku': '', 'downloadable': False, 'name': 'Абажур подвесной Паутина А-33'}]}
10.0.0.2 - - [29/Oct/2020 22:32:02] "POST /wp-json/wc/v3/products/batch/ HTTP/1.1" 200 -
Автор подозревает, что 1С 7.7 не дожидается завершения каждого запроса и создает некую очередь, что может приводить к потере тела запроса. Проблема усугубляется необходимостью обхода ограничений HTTPS на старой ОС Windows Server 2003, для чего используется Flask-прокси.
Для выполнения HTTP-запросов в 1С 7.7 часто используется COM-объект WinHttp.WinHttpRequest.5.1. Автор предоставил следующий код функции, отвечающей за отправку POST-запросов:
Функция POST_Req(Сервер, JSON, СпЗаголовки, пСтатус) Экспорт
WinHttp = СоздатьОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.open("POST",Сервер);
Для Н=1 По СпЗаголовки.РазмерСписка() Цикл
Значение="";
Тип = СпЗаголовки.ПолучитьЗначение(Н,Значение);
WinHttp.SetRequestHeader(СокрЛП(Тип), СокрЛП(Значение));
Сообщить(Тип + СимволТабуляции + Значение);
КонецЦикла;
Попытка
WinHttp.send(JSON);
пСтатус = WinHttp.status;
Если WinHttp.status = 200 Тогда
Сообщить(" HTTP " + WinHttp.Status + " " + WinHttp.StatusText);
Возврат WinHttp.ResponseText;
Иначе
Сообщить("Ошибка отправки запроса","!");
Сообщить("HTTP " + WinHttp.Status + " " + WinHttp.StatusText);
Возврат WinHttp.ResponseText;
//Возврат 0;
КонецЕсли;
Исключение
Сообщить(ОписаниеОшибки());
Возврат 0;
КонецПопытки;
КонецФункции
Давайте разберем ключевые аспекты этого кода:
WinHttp = СоздатьОбъект("WinHttp.WinHttpRequest.5.1"); – корректное создание объекта для HTTP-запросов.WinHttp.open("POST",Сервер); – здесь ключевым является отсутствие третьего параметра. По умолчанию, если этот параметр не указан, метод Open устанавливает запрос как синхронный. Это означает, что выполнение кода 1С должно приостановиться до получения ответа от сервера. Однако, несмотря на это, проблемы все равно возникают.СпЗаголовки устанавливает HTTP-заголовки. Крайне важно убедиться, что среди этих заголовков присутствует Content-Type: application/json. Отсутствие или некорректное значение этого заголовка может привести к тому, что принимающий сервер не сможет правильно распарсить тело запроса, воспринимая его как пустое или неверное.WinHttp.send(JSON); – отправляет тело запроса.Один из предложенных вариантов решения проблемы (сообщение 12) касался явного указания синхронности в методе Open:
Решение 1: Явное указание синхронности запроса
Попробуйте модифицировать строку WinHttp.open("POST",Сервер); на WinHttp.open("POST",Сервер,0);. Третий параметр метода Open принимает булево значение: True (или 1) для асинхронного запроса, False (или 0) для синхронного. Хотя по умолчанию отсутствие этого параметра подразумевает синхронность, явное указание 0 может устранить возможные неявные поведения или особенности объекта WinHttp в старых средах.
// ...
WinHttp = СоздатьОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.open("POST",Сервер,0); // Явно указываем синхронный режим
// ...
Эта модификация обеспечивает, что 1С будет ожидать ответа на каждый запрос перед тем, как перейти к следующему.
Проблема, когда тело запроса теряется для всех, кроме последнего запроса, является крайне необычной для синхронных HTTP-вызовов и может указывать на несколько точек отказа:
WinHttp.WinHttpRequest.5.1 создается на каждой итерации функции POST_Req, в некоторых случаях (особенно в очень старых или специфически настроенных средах) при быстром последовательном создании и использовании COM-объектов могут возникать проблемы с внутренним управлением ресурсами или состоянием объекта, что приводит к некорректной передаче тела запроса.Content-Type: Как упомянуто ранее, критически важно, чтобы для каждого запроса был установлен заголовок Content-Type: application/json. Без него сервер не будет знать, как интерпретировать входящие данные.Flask-прокси играет ключевую роль в этой схеме. Если проблемы возникают после того, как запрос ушел из 1С, следующим подозреваемым является прокси:
request.data) может быть пустым для не последних запросов. Хотя это маловероятно для стандартной настройки, это стоит иметь в виду.Чтобы точно определить, на каком этапе теряется JSON-тело, мы рекомендуем использовать следующие инструменты и подходы:
Fiddler – это мощный инструмент для перехвата и анализа HTTP/HTTPS трафика. Мы можем использовать его для:
В Fiddler мы сможем увидеть не только заголовки, но и полное содержимое тела каждого POST-запроса, а также коды ответов.
В ваш Flask-скрипт необходимо добавить расширенное логирование. Это поможет нам отследить, что именно получает Flask от 1С и что он отправляет в WooCommerce:
Content-Type).request.data или request.get_json()).Пример (упрощенный):
from flask import Flask, request, jsonify
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
@app.route('/wp-json/wc/v3/products/batch/', methods=['POST'])
def proxy_woocommerce():
app.logger.info(f"Received POST request from 1C to {request.url}")
app.logger.info(f"Headers: {request.headers}")
try:
# Попытка получить JSON, если Content-Type: application/json
json_data = request.get_json(silent=True)
if json_data:
app.logger.info(f"JSON Body received from 1C: {json_data}")
else:
# Если это не JSON, или парсинг не удался, пробуем сырые данные
raw_data = request.data
if raw_data:
app.logger.info(f"Raw Body received from 1C (length {len(raw_data)}): {raw_data.decode('utf-8', errors='ignore')}")
else:
app.logger.warning("No body received from 1C!")
except Exception as e:
app.logger.error(f"Error parsing JSON from 1C: {e}")
app.logger.info(f"Raw Body that caused error: {request.data.decode('utf-8', errors='ignore')}")
# ... Здесь логика пересылки на WooCommerce ...
# ... Логирование ответа от WooCommerce ...
return jsonify({"message": "Proxy processing..."}), 200
Это позволит нам точно понять, получает ли Flask тело запроса от 1С для каждого из проблемных вызовов.
Если после тщательной отладки проблема с пустым телом запроса не решается или если существуют более фундаментальные ограничения, рассмотрите следующие альтернативные подходы:
Вместо WinHttp.WinHttpRequest.5.1, попробуйте использовать Msxml2.ServerXMLHTTP (или Msxml2.ServerXMLHTTP.6.0, если доступно). Этот COM-объект часто рекомендуется для серверных приложений и может обладать большей стабильностью и лучшей поддержкой HTTP-протоколов, чем WinHttp, особенно в устаревших средах. Он устанавливается вместе с пакетами MSXML (Microsoft XML Core Services).
Функция POST_Req_ServerXMLHTTP(Сервер, JSON, СпЗаголовки, пСтатус) Экспорт
// Попробуйте ServerXMLHTTP.6.0, если доступно, иначе Msxml2.ServerXMLHTTP
WinHttp = СоздатьОбъект("Msxml2.ServerXMLHTTP");
WinHttp.open("POST",Сервер,0); // Явно синхронный режим
Для Н=1 По СпЗаголовки.РазмерСписка() Цикл
Значение="";
Тип = СпЗаголовки.ПолучитьЗначение(Н,Значение);
WinHttp.SetRequestHeader(СокрЛП(Тип), СокрЛП(Значение));
Сообщить(Тип + СимволТабуляции + Значение);
КонецЦикла;
// Важно для JSON:
WinHttp.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
Попытка
WinHttp.send(JSON);
пСтатус = WinHttp.status;
Если WinHttp.status = 200 Тогда
Сообщить(" HTTP " + WinHttp.Status + " " + WinHttp.StatusText);
Возврат WinHttp.ResponseText;
Иначе
Сообщить("Ошибка отправки запроса","!");
Сообщить("HTTP " + WinHttp.Status + " " + WinHttp.StatusText);
Возврат WinHttp.ResponseText;
КонецЕсли;
Исключение
Сообщить(ОписаниеОшибки());
Возврат 0;
КонецПопытки;
КонецФункции
Не забудьте явно установить заголовок Content-Type, включая указание кодировки UTF-8, так как ServerXMLHTTP может не делать этого автоматически.
Это очень надежный и гибкий подход. Вы можете использовать внешний инструмент командной строки cURL.exe, который доступен для Windows. Алгоритм будет следующим:
temp.json) на диске. Обязательно убедитесь, что файл сохраняется в кодировке UTF-8.cURL.exe через КомандаСистемы(), передавая ему путь к JSON-файлу и все необходимые параметры (URL, заголовки, метод POST).cURL отправляет запрос и записывает ответ в другой временный файл, который 1С затем может прочитать.Пример вызова cURL (концептуально):
// В 1С 7.7
ПутьКФайлуJSON = "C:\Temp\upload.json";
ТекстЗаписи = СоздатьОбъект("Текст");
ТекстЗаписи.ДобавитьСтроку(JSON);
ТекстЗаписи.Записать(ПутьКФайлуJSON, "UTF-8"); // Важно: сохраняем в UTF-8
URL = "https://db.test.ru/wp-json/wc/v3/products/batch/";
ConsumerKey = "ck_...";
ConsumerSecret = "cs_...";
Команда = "cURL -X POST """ + URL + "?"
+ "consumer_key=" + ConsumerKey + "&"
+ "consumer_secret=" + ConsumerSecret + """ "
+ "-H ""Content-Type: application/json"" "
+ "-d @""" + ПутьКФайлуJSON + """ "
+ "-o C:\Temp\response.txt";
КомандаСистемы(Команда);
// Затем читаем C:\Temp\response.txt для получения ответа
Этот подход полностью снимает с 1С проблемы с HTTP-стеком и HTTPS, перекладывая их на проверенный и актуальный инструмент.
Stunnel — это прокси-сервер, который создает зашифрованный туннель. Если основная проблема заключается именно в поддержке HTTPS на Windows Server 2003, stunnel может быть отличным решением:
stunnel на Windows Server 2003.stunnel как клиент, который будет прослушивать определенный локальный порт (например, 127.0.0.1:8080) по HTTP.127.0.0.1:8080 (по HTTP), stunnel перехватывает его, оборачивает в HTTPS-соединение и отправляет на целевой сервер (WooCommerce).Таким образом, 1С работает только с HTTP на локальной машине, а stunnel берет на себя всю сложность работы с HTTPS. Это упрощает логику 1С и позволяет избежать проблем с SSL/TLS на старой ОС.
Для 1С 7.7 существуют разработанные сообществом внешние компоненты (add-ins), которые предоставляют более современные и надежные функции для работы с HTTP/HTTPS, JSON и UTF-8. Например, на сайте Infostart можно найти такие компоненты. Использование готовой компоненты может значительно сократить время на разработку и отладку, а также обеспечить совместимость с актуальными стандартами безопасности и обмена данными.
Проблема потери JSON-тела при последовательных запросах из 1С 7.7 через Flask-прокси является комплексной и требует систематического подхода к отладке. Мы предлагаем действовать следующим образом:
WinHttp.open("POST",Сервер,0); в ваш код 1С.Content-Type: Убедитесь, что для каждого запроса корректно устанавливается заголовок Content-Type: application/json; charset=utf-8.Msxml2.ServerXMLHTTP, затем cURL или stunnel. Использование cURL или stunnel часто является наиболее надежным способом обхода ограничений старых систем и обеспечения корректного взаимодействия с современными веб-сервисами.Помните, что комплексная диагностика на каждом этапе взаимодействия (1С → Flask → WooCommerce) поможет локализовать проблему и применить наиболее эффективное решение. Успешная интеграция требует терпения и внимательности к деталям.