HTTP-сервисы 1С: создание, параметры, JSON, публикация и отладка

HTTP-сервис 1С — это обработчик внутри конфигурации, который отвечает на обычные HTTP-запросы: сайт, мобильное приложение, Python-скрипт или другая база 1С шлют GET или POST, а 1С возвращает JSON. В отличие от web-сервиса здесь нет SOAP и WSDL — только URL, метод и тело запроса. Ниже — полный цикл на платформе 8.3: как создать HTTP-сервис в конфигураторе, как устроены шаблоны URL и параметры, как принять и отдать JSON, опубликовать сервис на веб-сервере (в том числе из расширения), вызвать его из другой базы и отладить. В конце — таблица ошибок 404, 500 и 401 с причинами.

HTTP-сервис или web-сервис 1С: в чём разница

Оба объекта публикуют конфигурацию наружу, но устроены по-разному. Web-сервис работает по SOAP: клиент получает WSDL-описание, а обмен идёт XML-конвертами. HTTP-сервис ближе к REST: адрес ресурса, метод запроса и тело в любом формате, на практике — JSON.

КритерийHTTP-сервисWeb-сервис
ПротоколЧистый HTTP: GET, POST, PUT, DELETESOAP поверх HTTP
Формат данныхЛюбой, обычно JSONXML по схеме XDTO
Описание для клиентаНет, договорённость о форматеWSDL генерируется платформой
Адрес/hs/<корневой URL>/<шаблон>/ws/<имя>
Кому удобенСайт, мобильное приложение, Python, JSКорпоративные шины и старые интеграции на SOAP

Если с той стороны не 1С и не SOAP-клиент, берите HTTP-сервис: его можно вызвать из браузера, curl или requests без генерации прокси-классов.

Создание HTTP-сервиса в конфигураторе

HTTP-сервисы лежат в ветке «Общие → HTTP-сервисы». У объекта три уровня:

  1. Корневой URL — первая часть адреса после /hs/, например api. Обычно один корень на интеграцию.
  2. Шаблоны URL — ресурсы внутри корня: /products, /products/{code}. Фигурные скобки задают параметр пути, звёздочка в /products/* ловит любой хвост адреса.
  3. Методы — у каждого шаблона свои: GET для чтения, POST для создания, PUT и DELETE по необходимости. Каждому методу назначается функция-обработчик в модуле сервиса.

Корневой URL и шаблон вместе дают адрес ресурса. Для корня api и шаблона /products/{code} товар с кодом 000000125 будет доступен по адресу http://сервер/база/hs/api/products/000000125. Префикс версии удобно вынести в корень (api/v1): тогда вторую версию можно поднять рядом, не ломая старых клиентов.

Обработчик всегда принимает объект HTTPСервисЗапрос и возвращает HTTPСервисОтвет. Первым в сервис стоит добавить проверочный шаблон /echo: по нему сразу видно, опубликован ли сервис, ещё до того, как написана логика.

Функция EchoGET(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.УстановитьТелоИзСтроки("OK", КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
	Возврат Ответ;
КонецФункции

Когда у ресурса несколько методов, обработчики в модуле сервиса удобно оставить однострочными, а разбор по Запрос.HTTPМетод вынести в общий модуль. Так вся логика ресурса и перехват ошибок живут в одном месте:

// Модуль HTTP-сервиса
Функция ProductsGET(Запрос)
	Возврат HTTPСервисы.ProductsОбработатьЗапрос(Запрос);
КонецФункции

Функция ProductsPOST(Запрос)
	Возврат HTTPСервисы.ProductsОбработатьЗапрос(Запрос);
КонецФункции

// Общий модуль HTTPСервисы
Функция ProductsОбработатьЗапрос(Запрос) Экспорт
	ДанныеОтвета = СформироватьДанныеОтвета();
	Попытка
		Если Запрос.HTTPМетод = "GET" Тогда
			Возврат ProductsGET(Запрос, ДанныеОтвета);
		ИначеЕсли Запрос.HTTPМетод = "POST" Тогда
			Возврат ProductsPOST(Запрос, ДанныеОтвета);
		КонецЕсли;
	Исключение
		ЗаписьЖурналаРегистрации("HTTPСервисы", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
		Возврат ВернутьОшибку(ДанныеОтвета, "Внутренняя ошибка сервиса", 500);
	КонецПопытки;

	Возврат ВернутьОшибку(ДанныеОтвета, "Метод не поддерживается", 405);
КонецФункции

Полный текст ошибки уходит в журнал регистрации, а клиенту — короткое сообщение: стек вызовов и имена объектов конфигурации наружу отдавать незачем.

Параметры: путь, строка запроса и заголовки

Данные в HTTP-сервис приходят тремя путями, и у каждого своё свойство объекта запроса:

  • Запрос.ПараметрыURL — параметры из шаблона. Для /products/{code} значение берётся как Запрос.ПараметрыURL.Получить("code").
  • Запрос.ПараметрыЗапроса — всё, что после знака вопроса: ?limit=50&status=new. Значения всегда строки, числа приводите сами.
  • Запрос.Заголовки — служебные поля: Authorization, Content-Type, собственные ключи вроде X-Request-Id.

Правило, которое экономит часы отладки: идентификатор ресурса — в пути, фильтры и постраничность — в строке запроса. Один обработчик закрывает оба адреса: без кода в пути отдаёт весь список, с кодом — один товар.

Функция ProductsGET(Запрос, ДанныеОтвета)
	Код = Запрос.ПараметрыURL.Получить("code");
	Товары = СформироватьСписокТоваров(Код);

	Если Код <> Неопределено И Товары.Количество() = 0 Тогда
		Возврат ВернутьОшибку(ДанныеОтвета, "Товар не найден", 404);
	КонецЕсли;

	Возврат ВернутьДанные(ДанныеОтвета, Товары, 200);
КонецФункции

Функция СформироватьСписокТоваров(Код = Неопределено)
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Код КАК Код,
	|	Номенклатура.Наименование КАК Наименование
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	Номенклатура.Код = &Код";
	Если Код = Неопределено Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Номенклатура.Код = &Код", "ИСТИНА");
	Иначе
		Запрос.УстановитьПараметр("Код", Код);
	КонецЕсли;

	Товары = Новый Массив;
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Товар = Новый Структура;
		Товар.Вставить("uuid", XMLСтрока(Выборка.Ссылка));
		Товар.Вставить("code", Выборка.Код);
		Товар.Вставить("name", Выборка.Наименование);
		Товары.Добавить(Товар);
	КонецЦикла;

	Возврат Товары;
КонецФункции

Ссылку на элемент справочника в JSON напрямую не записать, поэтому она уходит строкой через XMLСтрока(Выборка.Ссылка) — получается UUID, по которому клиент потом найдёт тот же товар. Функции ВернутьДанные и ВернутьОшибку — общие помощники модуля, они в следующем разделе.

POST и JSON: принять тело и вернуть ответ

Тело запроса читается методом ПолучитьТелоКакСтроку(), а разбор JSON делает ПрочитатьJSON. Второй параметр Истина вернёт соответствие, Ложь — структуру. Соответствие надёжнее: структура падает на ключах, которые не подходят под имена переменных 1С, например updated-at или 1c_code.

Функция ProductsPOST(Запрос, ДанныеОтвета)
	ДанныеТовара = ПрочитатьСтрокуJSON(Запрос.ПолучитьТелоКакСтроку());
	Если ДанныеТовара = Неопределено Тогда
		Возврат ВернутьОшибку(ДанныеОтвета, "Тело запроса — не JSON", 400);
	КонецЕсли;

	Наименование = ДанныеТовара.Получить("name");
	Если Не ЗначениеЗаполнено(Наименование) Тогда
		Возврат ВернутьОшибку(ДанныеОтвета, "Не передан name", 422);
	КонецЕсли;

	НовыйТовар = Справочники.Номенклатура.СоздатьЭлемент();
	НовыйТовар.Наименование = Наименование;
	НовыйТовар.Записать();

	Возврат ВернутьДанные(ДанныеОтвета, Новый Структура("code", НовыйТовар.Код), 201);
КонецФункции

Все ответы сервиса удобно собирать в один конверт: {"response": …, "error": null} при успехе и {"response": null, "error": "…"} при ошибке. Клиент всегда знает, где искать данные, а где текст ошибки. Помощники для этого — пять коротких функций общего модуля:

Функция СформироватьДанныеОтвета()
	ДанныеОтвета = Новый Соответствие;
	ДанныеОтвета.Вставить("response", Неопределено);
	ДанныеОтвета.Вставить("error", Неопределено);
	Возврат ДанныеОтвета;
КонецФункции

Функция ВернутьДанные(ДанныеОтвета, Данные, Код)
	ДанныеОтвета["response"] = Данные;
	Возврат СформироватьОтвет(ДанныеОтвета, Код);
КонецФункции

Функция ВернутьОшибку(ДанныеОтвета, Ошибка, Код)
	ДанныеОтвета["error"] = Ошибка;
	Возврат СформироватьОтвет(ДанныеОтвета, Код);
КонецФункции

Функция СформироватьОтвет(Данные, Код)
	Ответ = Новый HTTPСервисОтвет(Код);
	Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
	Ответ.УстановитьТелоИзСтроки(ЗаписатьСтрокуJSON(Данные), КодировкаТекста.UTF8,
		ИспользованиеByteOrderMark.НеИспользовать);
	Возврат Ответ;
КонецФункции

Функция ЗаписатьСтрокуJSON(Данные) Экспорт
	ЗаписьJSON = Новый ЗаписьJSON;
	ЗаписьJSON.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет));

	НастройкиСериализации = Новый НастройкиСериализацииJSON;
	НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.ЛокальнаяДатаСоСмещением;
	НастройкиСериализации.ФорматСериализацииДаты = ФорматДатыJSON.ISO;

	ЗаписатьJSON(ЗаписьJSON, Данные, НастройкиСериализации);
	Возврат ЗаписьJSON.Закрыть();
КонецФункции

Разбор входящего тела — шестая функция. Битый JSON не роняет обработчик: функция пишет ошибку в журнал и возвращает Неопределено, а вызывающий код отвечает клиенту 400.

Функция ПрочитатьСтрокуJSON(СтрокаJSON, ИменаСвойствСДатами = Неопределено) Экспорт
	Если Не ЗначениеЗаполнено(СтрокаJSON) Тогда
		Возврат Неопределено;
	КонецЕсли;

	Попытка
		ЧтениеJSON = Новый ЧтениеJSON;
		ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
		Возврат ПрочитатьJSON(ЧтениеJSON, Истина, ИменаСвойствСДатами);
	Исключение
		ЗаписьЖурналаРегистрации("HTTPСервисы", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
		Возврат Неопределено;
	КонецПопытки;
КонецФункции

Три детали, которые чаще всего ломают интеграцию:

  • BOM в начале тела. Без параметра ИспользованиеByteOrderMark.НеИспользовать часть клиентов на JavaScript и Python падает на первом символе ответа.
  • Даты. Без настроек ЗаписатьJSON пишет дату без часового пояса, и клиент в другой зоне сдвинет её на несколько часов. Вариант ЛокальнаяДатаСоСмещением даёт 2026-10-13T10:00:00+03:00. В обратную сторону даты сами не распознаются: имена свойств с датами нужно передать третьим параметром ПрочитатьJSON, например updated_at и date, — тогда они придут типом «Дата», а не строкой.
  • Ссылки и перечисления. Напрямую в JSON они не пишутся — переводите в строку через XMLСтрока, как в примере с товарами. Как сериализовать объекты целиком через XDTO и чем это отличается от ручного JSON, разобрано в статье про XDTO, XML и JSON в 1С.

Коды ответа выбирайте по смыслу, а не «всегда 200 с полем error»: 201 — создано, 400 — тело не разобрали, 404 — ресурса нет, 422 — данные не прошли проверку. Клиенту на той стороне так проще обработать ошибку, а в логах веб-сервера сразу видно, что пошло не так.

Файлы и картинки через HTTP-сервис

JSON — не единственный формат. Архив обмена, PDF или картинку сервис принимает методом ПолучитьТелоКакДвоичныеДанные(), а отдаёт через УстановитьТелоИзДвоичныхДанных():

Функция ОбменPOST(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	Попытка
		ДвоичныеДанные = Запрос.ПолучитьТелоКакДвоичныеДанные();
		ДвоичныеДанные.Записать(КаталогВременныхФайлов() + "exchange.zip");
		Ответ.УстановитьТелоИзСтроки("OK");
	Исключение
		ЗаписьЖурналаРегистрации("HTTPСервисы", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
		Ответ.КодСостояния = 500;
		Ответ.УстановитьТелоИзСтроки("Не удалось сохранить файл");
	КонецПопытки;
	Возврат Ответ;
КонецФункции

Функция КартинкаGET(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.Заголовки.Вставить("Content-Type", "image/png");
	Ответ.УстановитьТелоИзДвоичныхДанных(БиблиотекаКартинок.Логотип.ПолучитьДвоичныеДанные());
	Возврат Ответ;
КонецФункции

Заголовок Content-Type для двоичного ответа обязателен: без image/png браузер не покажет картинку, а скачает её как файл без расширения.

Со стороны клиента файл отправляется тем же HTTPЗапрос, только тело — двоичные данные, а в заголовке — их тип:

HTTPЗапрос = Новый HTTPЗапрос("/clientbase/hs/ex");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/zip");
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ПолучитьОбщийМакет("ФайлОбмена"));
Результат = Соединение.ВызватьHTTPМетод("POST", HTTPЗапрос);

Если файл большой, не держите его в памяти целиком: у HTTPЗапрос есть УстановитьИмяФайлаТела(), и платформа читает тело прямо с диска.

Публикация HTTP-сервиса на веб-сервере

Пока база не опубликована, сервис недоступен снаружи. Публикация делается из конфигуратора («Администрирование → Публикация на веб-сервере», флажок «Публиковать HTTP-сервисы») или утилитой webinst. Результат — файл default.vrd в каталоге публикации. Фрагмент, который отвечает за HTTP-сервисы:

<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
	base="/trade"
	ib="Srvr=&quot;srv1c&quot;;Ref=&quot;trade&quot;;">
	<httpServices publishByDefault="true" publishExtensionsByDefault="true">
		<service name="API" rootUrl="api" enable="true"
			reuseSessions="autouse" sessionMaxAge="20"
			poolSize="10" poolTimeout="5"/>
	</httpServices>
</point>
  • publishByDefault публикует все HTTP-сервисы конфигурации, даже не перечисленные явно.
  • publishExtensionsByDefault нужен, если сервис добавлен расширением. Без него сервис из расширения отдаёт 404, хотя в конфигураторе всё на месте.
  • reuseSessions управляет переиспользованием сеансов: при частых коротких запросах autouse или use заметно снижает задержку, потому что сеанс 1С не создаётся заново на каждый вызов.

Итоговый адрес: http://сервер/trade/hs/api/products/000000125. Установка Apache и IIS, модуль wsap24, SSL и CORS для вызова сервиса из браузера разобраны в отдельном руководстве по публикации 1С на веб-сервере.

Вызов HTTP-сервиса из другой базы 1С

Обмен между базами через HTTP-сервис — частый сценарий: розница спрашивает остатки у центральной базы, мобильная база шлёт заказы в учётную. На стороне клиента работают HTTPСоединение и HTTPЗапрос. Удобно держать одну функцию на все методы и ресурсы:

Функция ОтправитьHTTPЗапрос(Метод, АдресРесурса, Данные = Неопределено) Экспорт
	Соединение = Новый HTTPСоединение("srv1c.local", 443, "exchange", ПарольОбмена(),
		, 30, Новый ЗащищенноеСоединениеOpenSSL);

	HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
	Если Данные <> Неопределено Тогда
		HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
		HTTPЗапрос.УстановитьТелоИзСтроки(ЗаписатьСтрокуJSON(Данные), КодировкаТекста.UTF8,
			ИспользованиеByteOrderMark.НеИспользовать);
	КонецЕсли;

	Результат = Соединение.ВызватьHTTPМетод(Метод, HTTPЗапрос);
	Возврат Новый Структура("Код, Тело", Результат.КодСостояния, Результат.ПолучитьТелоКакСтроку());
КонецФункции

Чтение и создание товара:

Ответ = ОтправитьHTTPЗапрос("GET", "/trade/hs/api/products/000000125");

Ответ = ОтправитьHTTPЗапрос("POST", "/trade/hs/api/products",
	Новый Структура("name", "Стол дубовый"));
Если Ответ.Код <> 201 Тогда
	ВызватьИсключение СтрШаблон("Сервис вернул %1: %2", Ответ.Код, Ответ.Тело);
КонецЕсли;
КодТовара = ПрочитатьСтрокуJSON(Ответ.Тело)["response"]["code"];

ВызватьHTTPМетод принимает имя метода строкой, поэтому одна функция покрывает GET, POST, PUT, DELETE и PATCH. У старых методов соединения названия свои: Получить — это GET, ОтправитьДляОбработки — POST, Записать — PUT, Удалить — DELETE. ЗаписатьСтрокуJSON и ПрочитатьСтрокуJSON — те же помощники, что и на стороне сервиса: их удобно держать в общем модуле, который есть в обеих базах. Таймаут в 30 секунд задан явно: без него зависший сервис держит клиентский сеанс до разрыва соединения.

Когда систем больше двух и нужны гарантии доставки и повтор сообщений, прямых HTTP-вызовов «точка-точка» уже мало. Когда хватает HTTP-сервиса, а когда пора брать брокер сообщений, сравнивается в таблице статьи про Kafka REST Proxy и 1С. А если основную логику удобнее писать на Python, а 1С оставить источником данных, посмотрите схему микросервиса на FastAPI рядом с 1С.

Авторизация HTTP-сервиса

По умолчанию HTTP-сервис требует аутентификацию пользователем информационной базы: клиент передаёт логин и пароль в заголовке Authorization: Basic, а обработчик выполняется с правами этого пользователя. Для обмена между своими базами этого достаточно, если соединение идёт по HTTPS, а у пользователя обмена урезанная роль.

Для внешних клиентов Basic неудобен: пароль уходит в каждом запросе, отозвать доступ одному клиенту нельзя. Начиная с 8.3.21 платформа умеет выпускать и проверять JWT штатным объектом ТокенДоступа — пошаговый разбор с выдачей и проверкой токена есть в статье о JWT-авторизации в HTTP-сервисе 1С.

Отладка HTTP-сервисов

Точка останова в модуле HTTP-сервиса сама по себе не сработает: код выполняется в сеансе веб-сервера, а не в вашем клиенте. Порядок такой:

  1. Включите в конфигураторе отладку по протоколу HTTP («Сервис → Параметры → Отладка») и запустите сервер отладки.
  2. Добавьте в default.vrd строку <debug enable="true" protocol="http" url="http://localhost:1550"/> с адресом сервера отладки и перезапустите веб-сервер.
  3. В конфигураторе откройте «Отладка → Подключение», в настройках автоматического подключения отметьте HTTP-сервисы.
  4. Поставьте точку останова и отправьте запрос из curl, Postman или тестовой обработки.

На боевом сервере отладку держать включённой не стоит. Там первым делом смотрите журнал регистрации: необработанное исключение обработчика попадает туда с полным стеком.

Ошибки HTTP-сервиса: 404, 500, 401

ОтветТипичная причинаЧто проверить
404Сервис не опубликован или адрес собран неверноПерепубликуйте базу после добавления сервиса; сверьте корневой URL и шаблон; для расширения — publishExtensionsByDefault
404 от самой 1САдрес совпал с корнем, но ни один шаблон не подошёлСлеш в начале шаблона, регистр, лишний сегмент пути
500Исключение в обработчике или обработчик ничего не вернулЖурнал регистрации; каждая ветка функции должна возвращать HTTPСервисОтвет
401Нет заголовка авторизации или неверная пара логин — парольПользователь существует в базе, у него есть право входа, кодировка пароля в Basic
Клиент не разбирает ответBOM в начале тела или неверный Content-TypeИспользованиеByteOrderMark.НеИспользовать и application/json; charset=utf-8

Где разобрать HTTP-сервисы на практике

Всё, что выше, — каркас: создание, параметры, JSON, публикация и вызов. В рабочей интеграции к нему добавляются версии API, журнал обмена, повторы при сбоях и защита от двойной отправки. Их удобнее один раз собрать руками на учебном проекте, чем чинить на живом обмене, — для этого есть курс по интеграции 1С через HTTP-сервисы.

HTTP-сервис — ещё и транспорт для ИИ-агентов: MCP-сервер внутри 1С публикуется именно так. Как это устроено, показано в разборе MCP-сервера для 1С.

Курс по теме статьи

HTTP в 1С для начинающих (вебинар)

Вебинар по работе с HTTP в 1С:Предприятии: создание и отладка HTTP-сервисов, POST- и GET-запросы, обработка ошибок и интеграция с внешними API и webhook.

3 000 ₽

Перейти к курсу «HTTP в 1С для начинающих (вебинар)»

Частые вопросы