HTTP-сервисы 1С — это способ сделать у информационной базы собственный REST API. В конфигураторе описывается сервис с корневым URL, внутри — шаблоны URL и HTTP-методы, у каждого метода — функция-обработчик, которая получает HTTPСервисЗапрос и возвращает HTTPСервисОтвет. После публикации базы на веб-сервере сервис доступен по адресу http://сервер/ИмяПубликации/hs/КорневойURL/Шаблон.

В статье соберём рабочий пример: список номенклатуры, продажи по ИНН контрагента и приём заказа методом POST. Потом опубликуем сервис и обратимся к нему из другой базы через HTTPСоединение. Код выложен целиком, с обработкой ошибок, которую я использую в реальных интеграциях.

HTTP-сервисы 1С или веб-сервисы: что выбрать

Платформа поддерживает два вида входящих интеграций:

HTTP-сервисы (REST)Веб-сервисы (SOAP)
ФорматЛюбой, обычно JSONXML по схеме
Описание контрактаСвоё (документ, OpenAPI вручную)WSDL генерирует платформа
АдресацияСвободные URL, методы GET/POST/PUT/DELETEОдна точка входа, операции
Кому удобноСайты, мобильные приложения, сервисы на любых языкахКорпоративные системы с SOAP-стеком

Для интеграции с сайтом, маркетплейсом, мобильным приложением или собственным сервисом на Python почти всегда выбирают HTTP-сервисы: их проще вызывать и отлаживать.

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

Возьмём типовую структуру: справочники Номенклатура и Контрагенты (с реквизитом ИНН), документ РеализацияТоваров с табличной частью Товары.

  1. В дереве метаданных: Общие → HTTP-сервисы → Добавить. Имя — Каталог, корневой URL — catalog.
  2. На вкладке «Шаблоны URL» добавьте шаблон Товары со строкой /products.
  3. У шаблона добавьте метод GET.
  4. В поле «Обработчик» нажмите кнопку открытия — платформа создаст функцию в модуле сервиса. Имя по умолчанию складывается из имени шаблона и метода.

Корневой URL и шаблоны я пишу латиницей в нижнем регистре: так адрес не придётся кодировать, и внешние разработчики не спотыкаются о кириллицу.

Обработчик GET: отдаём JSON

В модуле сервиса держу только разбор запроса и сборку ответа. Работа с данными — в отдельном серверном общем модуле, чтобы её можно было вызывать и тестировать без HTTP.

1С
Функция ТоварыGET(Запрос)

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

КонецФункции

// Формирует ответ HTTP-сервиса с телом в формате JSON.
Функция ОтветJSON(КодСостояния, Данные)

	ЗаписьJSON = Новый ЗаписьJSON;
	ЗаписьJSON.УстановитьСтроку();
	ЗаписатьJSON(ЗаписьJSON, Данные);

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

	Возврат Ответ;

КонецФункции

Явно отключаю BOM: часть внешних парсеров JSON спотыкается о маркер в начале тела.

Функция общего модуля ИнтеграцияКаталогСервер:

1С
// Возвращает массив структур с данными номенклатуры для выгрузки во внешние системы.
Функция СписокНоменклатуры() Экспорт

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

	Результат = Новый Массив;
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		// Новая структура на каждой итерации — иначе в массиве окажется одна и та же ссылка
		Позиция = Новый Структура;
		Позиция.Вставить("sku", Выборка.Артикул);
		Позиция.Вставить("name", Выборка.Наименование);
		Позиция.Вставить("type", Выборка.Вид);
		Позиция.Вставить("description", Выборка.Описание);
		Результат.Добавить(Позиция);
	КонецЦикла;

	Возврат Результат;

КонецФункции

ЗаписатьJSON сериализует строки, числа, булево, даты, Неопределено, массивы, структуры и соответствия со строковыми ключами. Ссылки на объекты так не запишутся — отдавайте представление, код или Строка(Ссылка.УникальныйИдентификатор()).

Параметры URL и коды состояния

Добавим в сервис шаблон Продажи со строкой /sales/{inn} и методом GET. Сегмент в фигурных скобках — параметр: всё, что придёт после /sales/, окажется в Запрос.ПараметрыURL. Параметры после ? читаются из Запрос.ПараметрыЗапроса.

1С
Функция ПродажиGET(Запрос)

	ИНН = Запрос.ПараметрыURL.Получить("inn");
	Если Не ЗначениеЗаполнено(ИНН) Тогда
		Возврат ОтветJSON(400, Новый Структура("error", "Не указан ИНН контрагента"));
	КонецЕсли;

	Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН);
	Если Контрагент.Пустая() Тогда
		Возврат ОтветJSON(404, Новый Структура("error", "Контрагент не найден"));
	КонецЕсли;

	// Необязательный параметр строки запроса: /sales/7700000000?from=2026-01-01
	ДатаНачала = Дата(1, 1, 1);
	СтрокаДаты = Запрос.ПараметрыЗапроса.Получить("from");
	Если ЗначениеЗаполнено(СтрокаДаты) Тогда
		Попытка
			// Ожидаем ГГГГ-ММ-ДД, приводим к виду ГГГГММДД
			ДатаНачала = Дата(СтрЗаменить(СтрокаДаты, "-", ""));
		Исключение
			Возврат ОтветJSON(400, Новый Структура("error", "Параметр from должен быть датой ГГГГ-ММ-ДД"));
		КонецПопытки;
	КонецЕсли;

	Данные = ИнтеграцияКаталогСервер.ПродажиКонтрагента(Контрагент, ДатаНачала);
	Возврат ОтветJSON(200, Данные);

КонецФункции

Некорректная дата не должна приводить к 500: ошибка во входных данных — это 400 с понятным текстом. Коды, которых хватает для большинства интеграций:

КодКогда возвращать
200Всё хорошо, в теле данные
201Объект создан (ответ на POST)
400Некорректный запрос: нет параметра, битый JSON
401 / 403Нет аутентификации / нет прав
404Объект не найден
409Конфликт, например дубль внешнего номера
500Ошибка на стороне 1С, подробности — в журнал регистрации

Приём данных методом POST

Для приёма заказа добавим шаблон /orders с методом POST. Тело читаем строкой и разбираем ПрочитатьJSON:

1С
Функция ЗаказыPOST(Запрос)

	ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
	Попытка
		ЧтениеJSON = Новый ЧтениеJSON;
		ЧтениеJSON.УстановитьСтроку(ТелоЗапроса);
		ДанныеЗаказа = ПрочитатьJSON(ЧтениеJSON);
		ЧтениеJSON.Закрыть();
	Исключение
		Возврат ОтветJSON(400, Новый Структура("error", "Тело запроса не является корректным JSON"));
	КонецПопытки;

	Если ТипЗнч(ДанныеЗаказа) <> Тип("Структура") Или Не ДанныеЗаказа.Свойство("externalId") Тогда
		Возврат ОтветJSON(400, Новый Структура("error", "Не передан externalId"));
	КонецЕсли;

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

	Возврат ОтветJSON(201, Новый Структура("number", НомерЗаказа));

КонецФункции

ПрочитатьJSON без второго параметра возвращает структуры. Если ключи могут быть не валидными идентификаторами 1С (с дефисами, цифрой в начале), передайте вторым параметром Истина и работайте с соответствием. Внешний идентификатор (externalId) нужен для идемпотентности: при повторной отправке того же заказа сервис должен найти существующий документ, а не создать дубль.

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

Без публикации сервис не доступен снаружи. В конфигураторе: Администрирование → Публикация на веб-сервере. На закладке «HTTP сервисы» включите «Публиковать HTTP сервисы по умолчанию» или отметьте нужные сервисы по отдельности. В файле default.vrd публикации это выглядит так:

xml
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       base="/erp"
       ib="Srvr=&quot;srv1c&quot;;Ref=&quot;erp&quot;;">
	<httpServices publishByDefault="true"/>
</point>

Проверка из командной строки:

bash
curl -u integration:Secret123 http://srv/erp/hs/catalog/products
curl -u integration:Secret123 "http://srv/erp/hs/catalog/sales/7700000000?from=2026-01-01"

Для интеграций заведите в базе отдельного пользователя с ролью, где есть только нужные права, и используйте HTTPS. Публикацию на Linux через Apache и утилиту webinst я разбирал в статье про установку сервера 1С на Ubuntu; настройку веб-доступа под ключ делаю в рамках услуги веб-доступ к 1С.

Обращение к HTTP-сервису из 1С

Для исходящих вызовов используются три объекта: HTTPСоединение (сервер, порт, учётные данные, таймаут, защищённое соединение), HTTPЗапрос (адрес ресурса, заголовки, тело) и HTTPОтвет (код, заголовки, тело ответа). Вызывать лучше на сервере: в веб-клиенте HTTPСоединение недоступно, а пароли не стоит держать на клиенте.

1С
&НаСервереБезКонтекста
Функция ПолучитьТоварыИзУчетнойБазы(Пользователь, Пароль)

	Таймаут = 30;
	Соединение = Новый HTTPСоединение("erp.example.local", 443, Пользователь, Пароль, , Таймаут,
		Новый ЗащищенноеСоединениеOpenSSL);

	HTTPЗапрос = Новый HTTPЗапрос("/erp/hs/catalog/products");
	HTTPЗапрос.Заголовки.Вставить("Accept", "application/json");

	Ответ = Соединение.Получить(HTTPЗапрос);
	ТелоОтвета = Ответ.ПолучитьТелоКакСтроку();

	Если Ответ.КодСостояния <> 200 Тогда
		ВызватьИсключение СтрШаблон("Сервис вернул код %1: %2", Ответ.КодСостояния, ТелоОтвета);
	КонецЕсли;

	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(ТелоОтвета);
	Товары = ПрочитатьJSON(ЧтениеJSON);
	ЧтениеJSON.Закрыть();

	Возврат Товары;

КонецФункции

Отправка заказа методом POST:

1С
&НаСервереБезКонтекста
Функция ОтправитьЗаказ(Соединение, ДанныеЗаказа)

	ЗаписьJSON = Новый ЗаписьJSON;
	ЗаписьJSON.УстановитьСтроку();
	ЗаписатьJSON(ЗаписьJSON, ДанныеЗаказа);

	HTTPЗапрос = Новый HTTPЗапрос("/erp/hs/catalog/orders");
	HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
	HTTPЗапрос.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), КодировкаТекста.UTF8,
		ИспользованиеByteOrderMark.НеИспользовать);

	Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
	Возврат Ответ.КодСостояния = 201;

КонецФункции

Basic-аутентификацию можно передать параметрами Пользователь и Пароль конструктора HTTPСоединение — собирать заголовок Authorization вручную через Base64Строка не обязательно. Для универсальных методов есть ВызватьHTTPМетод("PATCH", HTTPЗапрос).

Типичные ошибки при разработке HTTP-сервисов

  • Одна структура на весь массив. Структура создаётся до цикла, в цикле заполняется и добавляется в массив. В JSON получится N копий последней строки. Создавайте структуру внутри цикла.
  • 404 на правильный адрес. Сервис не отмечен при публикации, база не переопубликована после добавления сервиса или ошибка в регистре корневого URL.
  • Кракозябры в ответе. Не указана кодировка в Content-Type или в УстановитьТелоИзСтроки.
  • Ссылка в ЗаписатьJSON. Ошибка сериализации: в структуре оказалась СправочникСсылка. Отдавайте строки.
  • Исключение без обработки. Клиент получает 500 с HTML-страницей ошибки платформы. Ловите исключения, пишите их в журнал регистрации и возвращайте JSON с понятным текстом.
  • Тяжёлые операции в обработчике. Если создание объекта занимает минуты, внешний клиент отвалится по таймауту. Принимайте данные, кладите в очередь (регистр сведений) и обрабатывайте фоновым заданием.
  • Отсутствие идемпотентности. Повторная отправка создаёт дубли. Ищите объект по внешнему идентификатору.
HTTP-сервис — это контракт. Зафиксируйте адреса, форматы, коды ответов и правила повторной отправки до начала разработки, и интеграция переживёт обновления обеих сторон.

Когда обменов становится больше двух-трёх, отдельные HTTP-сервисы удобно дополнить очередью и мониторингом. Проектирую и делаю такие связки в рамках интеграции 1С по API и обмена между базами 1С.

Итог

  • HTTP-сервис состоит из корневого URL, шаблонов и методов; обработчик получает HTTPСервисЗапрос и возвращает HTTPСервисОтвет.
  • Параметры шаблона читаются из ПараметрыURL, строки запроса — из ПараметрыЗапроса, тело — ПолучитьТелоКакСтроку().
  • Отдавайте JSON с правильным Content-Type, осмысленными кодами состояния и без ссылок в данных.
  • Сервис начинает работать только после публикации базы с включёнными HTTP-сервисами; адрес — /ИмяПубликации/hs/КорневойURL/Шаблон.
  • Исходящие вызовы делайте на сервере через HTTPСоединение, с таймаутом, HTTPS и проверкой кода ответа.