API 1С — это механизмы, через которые внешняя программа (сайт, CRM, мобильное приложение, скрипт на Python) читает и записывает данные в базе 1С:Предприятия. Одного «API 1С:Предприятия» нет: платформа даёт пять способов подключиться к базе извне, и от выбора зависит объём кода и поведение базы под нагрузкой. Ниже — таблица выбора, публикация, права и примеры вызова из Postman, curl и Python.
Обратная задача, когда 1С сама вызывает чужой API через HTTPСоединение, разобрана в статье 1С и Яндекс Маркет: интеграция через API.
| Способ | Что нужно | Когда выбирать | Ограничения |
|---|---|---|---|
| OData (стандартный интерфейс) | Платформа 8.3.5+, веб-публикация, объекты в составе интерфейса | Отдать справочники, документы и регистры внешней системе или BI без программирования | Только стандартные операции, своих методов нет; клиент должен знать структуру метаданных |
| HTTP-сервис (свой REST) | 8.3.5+, объект в конфигурации или расширении (8.3.7+), публикация | Свой API под задачу: адреса, формат JSON и проверки решаете вы | Нужно программировать и сопровождать |
| Web-сервис (SOAP) | Публикация, типы в пакете XDTO | Другая сторона работает только по SOAP; связь баз 1С со строгой схемой | Громоздкий XML |
| COM-соединение | Windows, платформа той же версии, что сервер, та же разрядность | Скрипты во внутренней сети, пакетные загрузки | Долгое открытие соединения, не для сайта |
| Файловый обмен (XML, JSON, CSV) | Папка или FTP, регламентное задание | Нет постоянной связи, пакетный обмен между базами 1С | Задержка, нужен контроль ошибок загрузки |
Коротко: читать данные «как есть» — OData; всё, где есть бизнес-логика (создать заказ, проверить остаток), — HTTP-сервис; SOAP — когда его требует партнёр; COM и файлы — для внутренних задач.
OData, HTTP- и веб-сервисы работают только через веб-сервер:
trade, отметьте «Публиковать стандартный интерфейс OData» и нужные HTTP- и веб-сервисы.Настройки попадают в default.vrd в каталоге публикации. Если сервисы не отвечают, проверьте там <standardOdata enable="true"/> и <httpServices publishByDefault="true"/>.
Внешняя система входит в 1С как обычный пользователь и получает его права. Заведите технического пользователя:
api_user): кириллический логин в Basic-авторизации ломает часть HTTP-клиентов;Если публикация доступна из интернета, включите HTTPS и ограничьте доступ по IP. О ролях — на странице услуги по настройке прав доступа в 1С.
Стандартный интерфейс OData появился в платформе 8.3.5. Это автоматический REST: каждый объект, включённый в состав интерфейса, становится коллекцией со своим адресом:
http://srv/trade/odata/standard.odata/
http://srv/trade/odata/standard.odata/$metadata
http://srv/trade/odata/standard.odata/Catalog_Номенклатура
http://srv/trade/odata/standard.odata/Document_ЗаказКлиента
http://srv/trade/odata/standard.odata/InformationRegister_ЦеныНоменклатуры
Корень возвращает список доступных сущностей, $metadata — описание полей. После публикации список пуст, состав надо задать. В конфигурациях на БСП для этого есть форма настройки стандартного интерфейса OData (обычно «Администрирование» → «Синхронизация данных»). В своей конфигурации состав задают методом УстановитьСоставСтандартногоИнтерфейсаOData() под администратором:
// Модуль формы внешней обработки. Запускать под пользователем
// с административными правами. Платформа 8.3.5+.
&НаСервереБезКонтекста
Процедура ВключитьОбъектыВODataНаСервере()
// копируем текущий состав, чтобы не потерять уже включённые объекты
Состав = Новый Массив;
Для Каждого ОбъектМД Из ПолучитьСоставСтандартногоИнтерфейсаOData() Цикл
Состав.Добавить(ОбъектМД);
КонецЦикла;
Нужные = Новый Массив;
Нужные.Добавить(Метаданные.Справочники.Номенклатура);
Нужные.Добавить(Метаданные.РегистрыСведений.ЦеныНоменклатуры);
Для Каждого ОбъектМД Из Нужные Цикл
Если Состав.Найти(ОбъектМД) = Неопределено Тогда
Состав.Добавить(ОбъектМД);
КонецЕсли;
КонецЦикла;
УстановитьСоставСтандартногоИнтерфейсаOData(Состав);
КонецПроцедуры
Проверка в Postman: GET http://srv/trade/odata/standard.odata/Catalog_Номенклатура?$format=json&$top=5, вкладка Authorization → Basic Auth. Тот же запрос на Python:
import os
import requests
BASE = "http://srv/trade/odata/standard.odata"
AUTH = ("api_user", os.environ["ONEC_API_PASSWORD"])
url = (BASE + "/Catalog_Номенклатура"
"?$format=json&$top=5"
"&$select=Ref_Key,Code,Description"
"&$filter=DeletionMark eq false")
resp = requests.get(url, auth=AUTH, timeout=30)
resp.raise_for_status()
for item in resp.json()["value"]:
print(item["Ref_Key"], item["Code"], item["Description"])
Ref_Key, Code, Description), свои — как в конфигурации; ссылочные поля приходят как GUID с суффиксом _Key.Balance, SliceLast и др.) — остатки и цены доступны без своего кода.Post. Модуль объекта отрабатывает, логика формы — нет.$top и $select запрос к большому справочнику отдаёт всё целиком — заметная нагрузка на рабочую базу.Когда нужна логика — проверить входные данные, собрать ответ из нескольких таблиц, создать документ, — пишут HTTP-сервис: объект в ветке «Общие» → «HTTP-сервисы», доступен с платформы 8.3.5, а с 8.3.7 — и в расширении, без снятия типовой с поддержки. Адрес вызова:
http://<сервер>/<имя публикации>/hs/<корневой URL>/<шаблон URL>
http://srv/trade/hs/api/v1/products?sku=A-100
Ниже — сервис PublicAPI с корневым URL api: GET /v1/products ищет товары по артикулу, POST /v1/products/search принимает список артикулов в JSON. Функцию-обработчик указывают в свойстве «Обработчик» метода; конфигуратор предлагает имя вида ИмяШаблонаИмяМетода (здесь ТоварыGET). Она принимает Запрос и возвращает HTTPСервисОтвет. Создание сервиса в конфигураторе по шагам — в статье HTTP-сервис 1С: создание с нуля и пример.
// Модуль HTTP-сервиса PublicAPI. Корневой URL: api
// Шаблоны URL и методы:
// Товары /v1/products GET -> ТоварыGET
// ПоискТоваров /v1/products/search POST -> ПоискТоваровPOST
// Платформа 8.3.6+, режим совместимости не ниже 8.3.6 (ЗаписьJSON, ЧтениеJSON).
// Справочник Номенклатура с реквизитом Артикул есть в УТ 11, БП 3.0, УНФ, КА, ERP;
// в другой конфигурации поправьте текст запроса в ТекстЗапросаТоваров.
#Область ОбработчикиМетодов
// GET /v1/products?sku=A-100&limit=20
Функция ТоварыGET(Запрос)
Лимит = ЧислоИзПараметра(Запрос.ПараметрыЗапроса.Получить("limit"), 50, 500);
Если Лимит = Неопределено Тогда
Возврат ОтветОшибка(400, "Параметр limit должен быть целым числом больше нуля");
КонецЕсли;
Артикулы = Новый Массив;
Артикул = Запрос.ПараметрыЗапроса.Получить("sku");
Если ЗначениеЗаполнено(Артикул) Тогда
Артикулы.Добавить(Артикул);
КонецЕсли;
Попытка
Товары = НайтиТовары(Артикулы, Лимит);
Исключение
Возврат ОтветПриИсключении(ИнформацияОбОшибке());
КонецПопытки;
Возврат ОтветJSON(200, Новый Структура("items", Товары));
КонецФункции
// POST /v1/products/search, тело: {"skus": ["A-100", "B-200"]}
Функция ПоискТоваровPOST(Запрос)
Данные = ПрочитатьТелоJSON(Запрос);
Если ТипЗнч(Данные) <> Тип("Соответствие") Тогда
Возврат ОтветОшибка(400, "Тело запроса должно быть JSON-объектом");
КонецЕсли;
Список = Данные.Получить("skus");
Если ТипЗнч(Список) <> Тип("Массив") Тогда
Возврат ОтветОшибка(400, "Ожидается массив skus");
КонецЕсли;
Если Список.Количество() > 500 Тогда
Возврат ОтветОшибка(400, "Не больше 500 артикулов в одном запросе");
КонецЕсли;
Артикулы = Новый Массив;
Для Каждого Значение Из Список Цикл
Если ТипЗнч(Значение) = Тип("Строка") И ЗначениеЗаполнено(Значение) Тогда
Артикулы.Добавить(Значение);
КонецЕсли;
КонецЦикла;
Если Артикулы.Количество() = 0 Тогда
// пустой список означал бы "без отбора" - весь справочник
Возврат ОтветОшибка(400, "В skus нет ни одного непустого артикула");
КонецЕсли;
Попытка
Товары = НайтиТовары(Артикулы, 1000);
Исключение
Возврат ОтветПриИсключении(ИнформацияОбОшибке());
КонецПопытки;
Возврат ОтветJSON(200, Новый Структура("items", Товары));
КонецФункции
#КонецОбласти
#Область Данные
Функция НайтиТовары(Артикулы, Лимит)
ЗапросБД = Новый Запрос(ТекстЗапросаТоваров(Лимит));
ЗапросБД.УстановитьПараметр("БезОтбора", Артикулы.Количество() = 0);
ЗапросБД.УстановитьПараметр("Артикулы", Артикулы);
Товары = Новый Массив;
Выборка = ЗапросБД.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Товар = Новый Структура;
Товар.Вставить("id", Строка(Выборка.Ссылка.УникальныйИдентификатор()));
Товар.Вставить("name", Выборка.Наименование);
Товар.Вставить("sku", Выборка.Артикул);
Товары.Добавить(Товар);
КонецЦикла;
Возврат Товары;
КонецФункции
Функция ТекстЗапросаТоваров(Лимит)
// ЭтоГруппа есть только у иерархического справочника - иначе уберите условие
Текст =
"ВЫБРАТЬ ПЕРВЫЕ 50
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
| И НЕ Номенклатура.ЭтоГруппа
| И (&БезОтбора
| ИЛИ Номенклатура.Артикул В (&Артикулы))
|
|УПОРЯДОЧИТЬ ПО
| Наименование";
// в ПЕРВЫЕ нельзя передать параметр запроса, подставляем число в текст
Возврат СтрЗаменить(Текст, "ПЕРВЫЕ 50", "ПЕРВЫЕ " + Формат(Лимит, "ЧГ=0"));
КонецФункции
#КонецОбласти
#Область Служебные
Функция ПрочитатьТелоJSON(Запрос)
Попытка
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Запрос.ПолучитьТелоКакСтроку());
Данные = ПрочитатьJSON(Чтение, Истина); // Истина - объекты в Соответствие
Чтение.Закрыть();
Исключение
Возврат Неопределено; // пустое тело или невалидный JSON
КонецПопытки;
Возврат Данные;
КонецФункции
// Целое число из параметра строки запроса: ПоУмолчанию, если параметра нет,
// Неопределено, если передано не число.
Функция ЧислоИзПараметра(Значение, ПоУмолчанию, Максимум)
Если Не ЗначениеЗаполнено(Значение) Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Попытка
Результат = Число(Значение);
Исключение
Возврат Неопределено;
КонецПопытки;
Если Результат < 1 Или Результат <> Цел(Результат) Тогда
Возврат Неопределено;
КонецЕсли;
Возврат Мин(Результат, Максимум);
КонецФункции
Функция ОтветJSON(КодСостояния, Данные)
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Ответ.УстановитьТелоИзСтроки(Запись.Закрыть(), КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать);
Возврат Ответ;
КонецФункции
Функция ОтветОшибка(КодСостояния, Текст)
Возврат ОтветJSON(КодСостояния, Новый Структура("error", Текст));
КонецФункции
Функция ОтветПриИсключении(Информация)
// подробности - только в журнал, клиенту - короткий текст
ЗаписьЖурналаРегистрации("PublicAPI", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Информация));
Возврат ОтветОшибка(500, "Внутренняя ошибка сервиса");
КонецФункции
#КонецОбласти
Запрос.ПараметрыЗапроса — параметры после «?», отсутствующий ключ даёт Неопределено. Параметры из пути (шаблон /v1/products/{id}) лежат в Запрос.ПараметрыURL.СтрЗаменить: в ПЕРВЫЕ параметр запроса не передать. Сверху он ограничен, чтобы клиент не выгрузил справочник одним вызовом.ПрочитатьJSON(Чтение, Истина) читает объекты в Соответствие: в Структура ключ вроде "order-id" вызовет исключение.БезОтбора стал бы истиной и сервис вернул бы до тысячи товаров.ИспользованиеByteOrderMark.НеИспользовать (параметр есть с 8.3.6) явно отключает BOM: без него результат зависит от режима совместимости, а BOM перед JSON ломает разбор у части клиентов.В Postman для GET хватит адреса и Basic Auth. Для POST выберите Body → raw → JSON и вставьте {"skus": ["A-100", "B-200"]}; ответ — {"items": [...]} с полями id, name, sku. Из командной строки и Python:
# curl спросит пароль сам, в истории команд он не останется
curl -u api_user "http://srv/trade/hs/api/v1/products?sku=A-100&limit=10"
curl -u api_user -H "Content-Type: application/json" \
-d "{\"skus\": [\"A-100\", \"B-200\"]}" \
"http://srv/trade/hs/api/v1/products/search"
import os
import requests
BASE = "http://srv/trade/hs/api/v1"
AUTH = ("api_user", os.environ["ONEC_API_PASSWORD"])
resp = requests.post(BASE + "/products/search",
json={"skus": ["A-100", "B-200"]},
auth=AUTH, timeout=30)
if resp.ok:
for item in resp.json()["items"]:
print(item["sku"], item["name"], item["id"])
else:
print(resp.status_code, resp.text)
Если сервис вызывают часто, установите у него свойство «Повторное использование сеансов» в значение «Автоматически использовать» (платформа 8.3.9+): сеанс берётся из пула, а не открывается на каждый вызов. Значение «Использовать» помогает, только если клиент сам управляет сеансом: шлёт заголовок IBSession: start и возвращает полученную куку IBSession. Вызовы из примеров выше этого не делают.
Web-сервис — объект конфигурации с операциями, типы параметров описываются в пакете XDTO. WSDL платформа отдаёт сама по адресу http://srv/trade/ws/<имя файла публикации>?wsdl. В Python клиент обычно строят на библиотеке zeep, из другой базы 1С вызывают через WSОпределения и WSПрокси. Про типы — в статье XDTO в 1С: что это, фабрика и объекты XDTO.
Внешнее соединение даёт программе тот же доступ, что у серверного кода 1С: запросы, объекты, общие модули с флагом «Внешнее соединение». Условия: только Windows, платформа той же версии, что и сервер 1С, зарегистрированная comcntr.dll (regsvr32 от администратора), совпадение разрядности программы и компоненты. Соединение открывается долго, поэтому COM — для скриптов и пакетных загрузок, а не для сайта. Имена методов через COM доступны и по-английски, и по-русски.
import os
import win32com.client # pip install pywin32; разрядность Python = разрядности 1С
connector = win32com.client.Dispatch("V83.COMConnector")
conn = connector.Connect('Srvr="srv";Ref="trade";Usr="api_user";Pwd="{}";'
.format(os.environ["ONEC_API_PASSWORD"]))
# для файловой базы: 'File="D:\\Bases\\Trade";Usr="api_user";Pwd="...";'
query = conn.NewObject("Запрос")
query.Text = "ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК Cnt ИЗ Справочник.Номенклатура"
selection = query.Execute().Select()
if selection.Next():
print(selection.Cnt)
Одна сторона кладёт файл XML, JSON или CSV в папку или на FTP, регламентное задание 1С его загружает. Между базами 1С есть готовые механизмы — «Синхронизация данных» в формате EnterpriseData и правила Конвертации данных. Это уже не API, но для интеграции и обмена данными 1С между базами он надёжнее живых вызовов: обмен переживает недоступность одной из сторон. Сравнение — в статье Обмен данными 1С: способы обмена между базами.
/hs/... или /odata/.... Сервис не отмечен при публикации, ошибка в имени публикации или корневом URL, путь не совпал ни с одним шаблоном, либо не обновлена конфигурация базы данных.Authorization.standard.odata/.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig). Сервис отдаёт тело с BOM. Передайте третьим параметром УстановитьТелоИзСтроки значение ИспользованиеByteOrderMark.НеИспользовать.UnicodeEncodeError: 'latin-1' codec can't encode characters при авторизации. Логин 1С на кириллице, а requests кодирует его в latin-1. Заведите пользователя с латинским именем.comcntr.dll не зарегистрирована или не совпадает разрядность: 32-битный Python не видит 64-битный коннектор. Отказ даёт и версия платформы на клиенте, отличная от серверной.Да, стандартный интерфейс OData: опубликуйте базу и включите объекты в состав, конфигурацию менять не нужно. Но он отдаёт «сырые» справочники, документы и регистры. Метод вроде «создать заказ с проверкой остатка» пишут в HTTP-сервисе.
Да. OData работает поверх типовой без изменений, а HTTP-сервис с платформы 8.3.7 можно разместить в расширении.
Библиотекой requests к OData или к своему HTTP-сервису — примеры выше. COM через pywin32 работает только на Windows с установленной платформой.
Да, файловая база публикуется так же, как серверная. Но при частых запросах от нескольких клиентов она упирается в блокировки, для постоянной интеграции нужен клиент-серверный вариант.
API — вызов «здесь и сейчас»: сайт спросил остаток и сразу получил ответ. Обмен — пакетная передача изменений по расписанию с повтором после сбоя. Сайту и приложениям нужен API, двум базам 1С — обмен.
Если 1С нужно связать с сайтом, CRM или приложением, а своего программиста 1С нет, отдайте задачу исполнителю: он опубликует базу, заведёт пользователя с нужными правами и напишет HTTP-сервис под ваш формат. Посмотрите услуги по доработке 1С и обмен 1С с сайтом или сразу разместите задачу: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.