- Tutorial
2. Реализация API на стороне 1С.
Пособий как создать HTTP-сервис в интернете предостаточно. Поэтому сразу опишу реализацию. Сервис у нас состоит из трех шаблонов URL:
- Справочники — /v1/catalogs/{Справочник}/{Действие}
- Документы — /v1/documents/{Документ}/{Действие}
- Сервисы — /v1/services/{Действие}
Для того чтобы не раздувать статью, рассмотрим пример со справочником «Номенклатура». 1С нам не позволяет иметь разные пути для HTTP-методов По этому шаблон URL будет (/v1/catalogs/wares/{Действие}, для которого определены два HTTP метода GET, POST. В других шаблонах также будем использовать GET, POST.
- /v1 — позволит нам в будущем поддерживать несколько вариантов API. Мы сможем добавить шаблон URL Справочники_v2 и соответственно путь начать с /v2
- /catalogs/wares/ — catalogs объединяет все справочники. Это статичная часть пути. wares соответствует справочнику «Номенклатура». 1С поместит «wares» в ПараметрыURL. Тут надо отметить, что пути мы придумываем сами. И как их потом обрабатывать в 1С думаем тоже сами
- {Действие} — сюда мы будем передавать что же мы хотим получить от 1С. На пример getByParam или getAll
С точки зрения Retrofit baseUrl будет выглядеть примерно так "http://192.168.0.1/unf/hs/inntsd/v1/" где:
- 192.168.0.1 — Сервер где опубликован HTTP-сервис
- /unf — имя базы данных 1С
- /hs — статическая часть для HTTP-сервисов
- /inntsd — корневой URL для всего сервиса. Обзываем как хотим
Рассмотрим процедуру получения запроса.
Функция СправочникиGet(Запрос)
//Разбираем параметры запроса. Заполнение структуры выполняется одинаково и для Get и для //Post
СтруктураЗапросаСправочник = ВернутьПараметрыЗапросаСправочник(Запрос, "GET");
//middleware. Проверим что пришли все необходимые данные.
РезультатПроверки = ПроверитьСтруктуруЗапроса(СтруктураЗапросаСправочник);
Если РезультатПроверки Тогда
СтруктураОтвета = ОбработатьЗапросСправочник(СтруктураЗапросаСправочник);
Функция ВернутьПараметрыЗапросаСправочник(Запрос, МетодЗапроса) Экспорт
Справочник = ПолучитьИмяСправочника(Запрос); // получим переменную {Справочник}
Действие = ПолучитьДействиеСправочник(Запрос); // получим переменную {Действие}
ПараметрыСтроки = ПолучитьПараметрыИзСтроки(Запрос); // получим список из Параметров URL. В функции одна строка Возврат Запрос.ПараметрыURL
ПараметрыТела =ПолучитьПараметрыИзТела(Запрос); // Советую взглянуть на эту функцию поближе. Поможет понять почему в Firefox работает, а Google Chrome нет
СтруктураЗапроса = Новый Структура;
СтруктураЗапроса.Вставить("МетодЗапроса", МетодЗапроса);
СтруктураЗапроса.Вставить("Действие", Действие);
СтруктураЗапроса.Вставить("Справочник", Справочник);
СтруктураЗапроса.Вставить("ПараметрыСтроки", ПараметрыСтроки);
СтруктураЗапроса.Вставить("ПараметрыТела", ПараметрыТела);
Возврат СтруктураЗапроса;
КонецФункции
Функция ПолучитьПараметрыИзТела(Запрос)
ТипСообщения = Запрос.Заголовки.Получить("Content-Type");
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ОшибкаЧтения", Ложь);
//Разные браузеры по разному отдают "Content-Type","content-type", "Content-type"
Если ТипСообщения = Неопределено тогда
ТипСообщения = Запрос.Заголовки.Получить("content-type");
КонецЕсли;
Если ТипСообщения = Неопределено тогда
ТипСообщения = Запрос.Заголовки.Получить("Сontent-type");
КонецЕсли;
Если ТипСообщения = Неопределено Тогда
СтруктураДанных.Вставить("ОшибкаЧтения", Истина);
Возврат СтруктураДанных;
КонецЕсли;
Если Найти(Нрег(ТипСообщения), "multipart/form-data") > 0 тогда
СтруктураДанных = ЗаполнитьСтруктуруДополнительныхПараметровИзТела(Запрос);
ИначеЕсли Найти(Нрег(ТипСообщения), "application/json") > 0 тогда
СтруктураДанных = ЗаполнитьСтруктуруДополнительныхПараметровJSON(Запрос);
Иначе
СтруктураДанных.Вставить("ОшибкаЧтения", Истина);
КонецЕсли;
Возврат СтруктураДанных;
КонецФункции
//С новыми методами для работы с JSON работать стало одно удовольствие. Даже с датами проблем нет.
Функция ЗаполнитьСтруктуруДополнительныхПараметровJSON(Запрос);
ЧтениеJSON = Новый ЧтениеJSON;
Данные = Запрос.ПолучитьТелоКакСтроку();
ЧтениеJSON.УстановитьСтроку(Данные);
Попытка
СтруктураДанных = ПрочитатьJSON(ЧтениеJSON,,"sampleDate",ФорматДатыJSON.ISO);
СтруктураДанных.Вставить("ОшибкаЧтения", Ложь);
Исключение
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ОшибкаЧтения", Истина);
КонецПопытки;
ЧтениеJSON.Закрыть();
Возврат СтруктураДанных;
КонецФункции
В данном примере есть много вредных советов. Например ИмяСправочника, Действие необходимо переделать в перечисление или в новый справочник с соответствиями. Но как это элегантно реализовать в дополнении я еще не решил.
Разберем запрос getByParam. Полный адрес: /v1/catalogs/wares/getByParam?prop=byCode&comparison=similarly&searchString=239
Выясним с каким справочником мы работаем
Функция ОбработатьЗапросСправочник(СтруктураЗапроса)
Если СтруктураЗапроса.Справочник = "Номенклатура" Тогда
СтруктураОтвета = ОбработатьЗапросНоменклатура(СтруктураЗапроса);
ИначеЕсли СтруктураЗапроса.Справочник = "Склады" Тогда
СтруктураОтвета = ОбработатьЗапросСклады(СтруктураЗапроса)
Иначе
СтруктураОтвета = ПодготовитьСтруктуруОтвета(100, "Не найден обработчик справочника " + СтруктураЗапроса.Справочник);
КонецЕсли;
Возврат СтруктураОтвета;
КонецФункции
Получаем метод запроса. И проверяем что к методу прилагаются все параметры. Здесь надо отметить глубокую проверку я не делаю. Так как пишу и для 1С, и для Android.
Функция ОбработатьЗапросНоменклатура(СтруктураЗапроса)
Если СтруктураЗапроса.Действие = "ПолучитьПоПараметрам" Тогда
РеквизитСравнения = СтруктураЗапроса.ПараметрыСтроки.Получить("prop");
ТипСравнения = СтруктураЗапроса.ПараметрыСтроки.Получить("comparison");
СтрокаПоиска = СтруктураЗапроса.ПараметрыСтроки.Получить("searchString");
Если (РеквизитСравнения = Неопределено) или (ТипСравнения = Неопределено) или (СтрокаПоиска = Неопределено) Тогда
СтруктураОтвета = ПодготовитьСтруктуруОтвета(103, "Не заданы поля Реквизит сравнения и/или Тип сравнения или нет строки поиска");
Возврат СтруктураОтвета;
КонецЕсли;
Данные = НоменклатураВернутьЭлементыПоПараметрам(СтрокаПоиска, РеквизитСравнения, ТипСравнения);
СтруктураОтвета = ПодготовитьСтруктуруОтвета(0, "");
СтруктураОтвета.Вставить("payload", Данные);
ИначеЕсли СтруктураЗапроса.Действие = "ПолучитьВсеЭлементы" Тогда
Данные = НоменклатураВернутьВсеЭлементы();
СтруктураОтвета = ПодготовитьСтруктуруОтвета(0, "");
СтруктураОтвета.Вставить("payload", Данные);
Иначе
СтруктураОтвета = ПодготовитьСтруктуруОтвета("102", "Не наден обработчик для действия " + СтруктураЗапроса.Действие);
КонецЕсли;
Возврат СтруктураОтвета;
КонецФункции
Если все хорошо. Подготавливаем данные для ответа.
Функция НоменклатураВернутьЭлементыПоПараметрам(СтрокаПоиска, Реквизит, ТипСравнения)
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Номенклатура.Код КАК Код,
| Номенклатура.Артикул КАК Артикул,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.НаименованиеПолное КАК НаименованиеПолное,
| Номенклатура.ЕдиницаИзмерения.Наименование КАК ЕдиницаИзмеренияНаименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
| И {Условие}";
стрУсловие = "";
Если НРег(Реквизит) = НРег("byCode") тогда
стрУсловие = "Номенклатура.Код";
ИначеЕсли НРег(Реквизит) = НРег("byArticle") тогда
стрУсловие = "Номенклатура.Артикул";
ИначеЕсли НРег(Реквизит) = НРег("byName") тогда
стрУсловие = "Номенклатура.Наименование";
КонецЕсли;
Если НРег(ТипСравнения) = НРег("equally") тогда
стрУсловие = стрУсловие + " = &Параметр";
ИначеЕсли НРег(ТипСравнения) = НРег("similarly") тогда
стрУсловие = стрУсловие + " ПОДОБНО &Параметр";
КонецЕсли;
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{Условие}", стрУсловие);
Запрос.Текст = ТекстЗапроса;
Если НРег(ТипСравнения) = НРег("similarly") тогда
СтрокаПоиска = "%" + СтрокаПоиска + "%";
КонецЕсли;
Запрос.УстановитьПараметр("Параметр", СтрокаПоиска);
ТЗ = Запрос.Выполнить().Выгрузить();
КолВоЭлементов = ТЗ.Количество();
МассивЭлементов = Новый Массив;
Для каждого СтрокаТЗ из ТЗ Цикл
МассивЭлементов.Добавить(Новый Структура("code,article, name, fullName, unit",
СтрокаТЗ.Код,
СтрокаТЗ.Артикул,
СтрокаТЗ.Наименование,
СтрокаТЗ.НаименованиеПолное,
СтрокаТЗ.ЕдиницаИзмеренияНаименование));
КонецЦикла;
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("quantity", КолВоЭлементов);
СтруктураДанных.Вставить("wares", МассивЭлементов);
Возврат СтруктураДанных;
КонецФункции
И наконец объединяем все вместе. Помещаем это в JSON и отправляем ответ.
СтруктураОтвета = ПодготовитьСтруктуруОтвета(0, "");
СтруктураОтвета.Вставить("payload", Данные);
Возврат СтруктураОтвета;
Функция ПодготовитьОтвет(КодОтвета, СтруктураОтвета) Экспорт
ТелоОтвета = ПолучитьСтрокуТелаОтвета(СтруктураОтвета);
HTTPОтвет = Новый HTTPСервисОтвет(КодОтвета);
HTTPОтвет.Заголовки["Content-Type"] = "application/json; charset=utf-8"; //сообщаем что это json UTF-8
// ToDo CORS
//HTTPОтвет.Заголовки["Access-Control-Allow-Origin"] = Оригин;
//HTTPОтвет.Заголовки["Access-Control-Allow-Credentials"] = "true";
HTTPОтвет.УстановитьТелоИзСтроки(ТелоОтвета);
Возврат HTTPОтвет;
КонецФункции
Функция ПолучитьСтрокуТелаОтвета(СтруктураТела)
ПараметрыJSON = Новый ПараметрыЗаписиJSON(, Символы.Таб,,,,,,,);
ТелоОтвета = новый ЗаписьJSON;
ТелоОтвета.ПроверятьСтруктуру = Ложь;
ТелоОтвета.УстановитьСтроку(ПараметрыJSON);
НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.ЛокальнаяДата;
НастройкиСериализации.ФорматСериализацииДаты = ФорматДатыJSON.ISO;
ЗаписатьJSON(ТелоОтвета, СтруктураТела, НастройкиСериализации);
СтрокаТелаОтвета = ТелоОтвета.Закрыть();
Возврат СтрокаТелаОтвета;
КонецФункции
И отдаем это все на клиента.
Ответ = ПодготовитьОтвет(200, СтруктураОтвета)
Возврат Ответ;
Вне зависимости что мы получили от 1С. Всегда стараемся ответить правильно.
Функция ПодготовитьСтруктуруОтвета(КодОшибки, ТекстОшибки)
СтруктураОтвета = Новый Структура;
СтруктураРезультата = Новый Структура();
СтруктураРезультата.Вставить("code", КодОшибки);
СтруктураРезультата.Вставить("description", ТекстОшибки);
СтруктураОтвета.Вставить("result", СтруктураРезультата);
СтруктураОтвета.Вставить("payload", Новый Структура);
Возврат СтруктураОтвета;
КонецФункции
На этом все. Все остальные методы реализованы похожим способом. Задавайте вопросы.С радостью отвечу.